atkbdc.c revision 42421
142421Syokota/*- 242421Syokota * Copyright (c) 1996-1999 342421Syokota * Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp) 442421Syokota * All rights reserved. 542421Syokota * 642421Syokota * Redistribution and use in source and binary forms, with or without 742421Syokota * modification, are permitted provided that the following conditions 842421Syokota * are met: 942421Syokota * 1. Redistributions of source code must retain the above copyright 1042421Syokota * notice, this list of conditions and the following disclaimer. 1142421Syokota * 2. Redistributions in binary form must reproduce the above copyright 1242421Syokota * notice, this list of conditions and the following disclaimer in the 1342421Syokota * documentation and/or other materials provided with the distribution. 1442421Syokota * 3. The name of the author may not be used to endorse or promote 1542421Syokota * products derived from this software without specific prior written 1642421Syokota * permission. 1742421Syokota * 1842421Syokota * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1942421Syokota * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2042421Syokota * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2142421Syokota * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2242421Syokota * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2342421Syokota * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2442421Syokota * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2542421Syokota * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2642421Syokota * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2742421Syokota * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2842421Syokota * SUCH DAMAGE. 2942421Syokota * 3042421Syokota * $Id: $ 3142421Syokota * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp 3242421Syokota */ 3342421Syokota 3442421Syokota#include "atkbdc.h" 3542421Syokota#include "opt_kbd.h" 3642421Syokota 3742421Syokota#include <sys/param.h> 3842421Syokota#include <sys/systm.h> 3942421Syokota#include <sys/kernel.h> 4042421Syokota#include <sys/malloc.h> 4142421Syokota#include <sys/syslog.h> 4242421Syokota 4342421Syokota#include <machine/clock.h> 4442421Syokota 4542421Syokota#include <dev/kbd/atkbdcreg.h> 4642421Syokota 4742421Syokota#ifndef __i386__ 4842421Syokota#include <isa/isareg.h> 4942421Syokota#else 5042421Syokota#include <i386/isa/isa.h> 5142421Syokota#endif 5242421Syokota 5342421Syokota/* constants */ 5442421Syokota 5542421Syokota#define MAXKBDC MAX(NATKBDC, 1) 5642421Syokota 5742421Syokota/* macros */ 5842421Syokota 5942421Syokota#ifndef MAX 6042421Syokota#define MAX(x, y) ((x) > (y) ? (x) : (y)) 6142421Syokota#endif 6242421Syokota 6342421Syokota#define kbdcp(p) ((atkbdc_softc_t *)(p)) 6442421Syokota#define nextq(i) (((i) + 1) % KBDQ_BUFSIZE) 6542421Syokota#define availq(q) ((q)->head != (q)->tail) 6642421Syokota#if KBDIO_DEBUG >= 2 6742421Syokota#define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0) 6842421Syokota#else 6942421Syokota#define emptyq(q) ((q)->tail = (q)->head = 0) 7042421Syokota#endif 7142421Syokota 7242421Syokota/* local variables */ 7342421Syokota 7442421Syokota/* 7542421Syokota * We always need at least one copy of the kbdc_softc struct for the 7642421Syokota * low-level console. As the low-level console accesses the keyboard 7742421Syokota * controller before kbdc, and all other devices, is probed, we 7842421Syokota * statically allocate one entry. XXX 7942421Syokota */ 8042421Syokotastatic atkbdc_softc_t default_kbdc; 8142421Syokotastatic atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc }; 8242421Syokota 8342421Syokotastatic int verbose = KBDIO_DEBUG; 8442421Syokota 8542421Syokota/* function prototypes */ 8642421Syokota 8742421Syokotastatic int atkbdc_setup(atkbdc_softc_t *sc, int port); 8842421Syokotastatic int addq(kqueue *q, int c); 8942421Syokotastatic int removeq(kqueue *q); 9042421Syokotastatic int wait_while_controller_busy(atkbdc_softc_t *kbdc); 9142421Syokotastatic int wait_for_data(atkbdc_softc_t *kbdc); 9242421Syokotastatic int wait_for_kbd_data(atkbdc_softc_t *kbdc); 9342421Syokotastatic int wait_for_kbd_ack(atkbdc_softc_t *kbdc); 9442421Syokotastatic int wait_for_aux_data(atkbdc_softc_t *kbdc); 9542421Syokotastatic int wait_for_aux_ack(atkbdc_softc_t *kbdc); 9642421Syokota 9742421Syokota#if NATKBDC > 0 9842421Syokota 9942421Syokotaatkbdc_softc_t 10042421Syokota*atkbdc_get_softc(int unit) 10142421Syokota{ 10242421Syokota atkbdc_softc_t *sc; 10342421Syokota 10442421Syokota if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0])) 10542421Syokota return NULL; 10642421Syokota sc = atkbdc_softc[unit]; 10742421Syokota if (sc == NULL) { 10842421Syokota sc = atkbdc_softc[unit] 10942421Syokota = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); 11042421Syokota if (sc == NULL) 11142421Syokota return NULL; 11242421Syokota bzero(sc, sizeof(*sc)); 11342421Syokota sc->port = -1; /* XXX */ 11442421Syokota } 11542421Syokota return sc; 11642421Syokota} 11742421Syokota 11842421Syokotaint 11942421Syokotaatkbdc_probe_unit(atkbdc_softc_t *sc, int unit, int port) 12042421Syokota{ 12142421Syokota return atkbdc_setup(sc, port); 12242421Syokota} 12342421Syokota 12442421Syokota#endif /* NATKBDC > 0 */ 12542421Syokota 12642421Syokota/* the backdoor to the keyboard controller! XXX */ 12742421Syokotaint 12842421Syokotaatkbdc_configure(void) 12942421Syokota{ 13042421Syokota return atkbdc_setup(atkbdc_softc[0], -1); 13142421Syokota} 13242421Syokota 13342421Syokotastatic int 13442421Syokotaatkbdc_setup(atkbdc_softc_t *sc, int port) 13542421Syokota{ 13642421Syokota if (port <= 0) 13742421Syokota port = IO_KBD; 13842421Syokota 13942421Syokota if (sc->port <= 0) { 14042421Syokota sc->command_byte = -1; 14142421Syokota sc->command_mask = 0; 14242421Syokota sc->lock = FALSE; 14342421Syokota sc->kbd.head = sc->kbd.tail = 0; 14442421Syokota sc->aux.head = sc->aux.tail = 0; 14542421Syokota#if KBDIO_DEBUG >= 2 14642421Syokota sc->kbd.call_count = 0; 14742421Syokota sc->kbd.qcount = sc->kbd.max_qcount = 0; 14842421Syokota sc->aux.call_count = 0; 14942421Syokota sc->aux.qcount = sc->aux.max_qcount = 0; 15042421Syokota#endif 15142421Syokota } 15242421Syokota sc->port = port; /* may override the previous value */ 15342421Syokota return 0; 15442421Syokota} 15542421Syokota 15642421Syokota/* associate a port number with a KBDC */ 15742421Syokota 15842421SyokotaKBDC 15942421Syokotakbdc_open(int port) 16042421Syokota{ 16142421Syokota int s; 16242421Syokota int i; 16342421Syokota 16442421Syokota if (port <= 0) 16542421Syokota port = IO_KBD; 16642421Syokota 16742421Syokota s = spltty(); 16842421Syokota for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) { 16942421Syokota if (atkbdc_softc[i] == NULL) 17042421Syokota continue; 17142421Syokota if (atkbdc_softc[i]->port == port) { 17242421Syokota splx(s); 17342421Syokota return (KBDC)atkbdc_softc[i]; 17442421Syokota } 17542421Syokota if (atkbdc_softc[i]->port <= 0) { 17642421Syokota if (atkbdc_setup(atkbdc_softc[i], port)) 17742421Syokota break; 17842421Syokota splx(s); 17942421Syokota return (KBDC)atkbdc_softc[i]; 18042421Syokota } 18142421Syokota } 18242421Syokota splx(s); 18342421Syokota return NULL; 18442421Syokota} 18542421Syokota 18642421Syokota/* 18742421Syokota * I/O access arbitration in `kbdio' 18842421Syokota * 18942421Syokota * The `kbdio' module uses a simplistic convention to arbitrate 19042421Syokota * I/O access to the controller/keyboard/mouse. The convention requires 19142421Syokota * close cooperation of the calling device driver. 19242421Syokota * 19342421Syokota * The device driver which utilizes the `kbdio' module are assumed to 19442421Syokota * have the following set of routines. 19542421Syokota * a. An interrupt handler (the bottom half of the driver). 19642421Syokota * b. Timeout routines which may briefly polls the keyboard controller. 19742421Syokota * c. Routines outside interrupt context (the top half of the driver). 19842421Syokota * They should follow the rules below: 19942421Syokota * 1. The interrupt handler may assume that it always has full access 20042421Syokota * to the controller/keyboard/mouse. 20142421Syokota * 2. The other routines must issue `spltty()' if they wish to 20242421Syokota * prevent the interrupt handler from accessing 20342421Syokota * the controller/keyboard/mouse. 20442421Syokota * 3. The timeout routines and the top half routines of the device driver 20542421Syokota * arbitrate I/O access by observing the lock flag in `kbdio'. 20642421Syokota * The flag is manipulated via `kbdc_lock()'; when one wants to 20742421Syokota * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if 20842421Syokota * the call returns with TRUE. Otherwise the caller must back off. 20942421Syokota * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion 21042421Syokota * is finished. This mechanism does not prevent the interrupt 21142421Syokota * handler from being invoked at any time and carrying out I/O. 21242421Syokota * Therefore, `spltty()' must be strategically placed in the device 21342421Syokota * driver code. Also note that the timeout routine may interrupt 21442421Syokota * `kbdc_lock()' called by the top half of the driver, but this 21542421Syokota * interruption is OK so long as the timeout routine observes the 21642421Syokota * the rule 4 below. 21742421Syokota * 4. The interrupt and timeout routines should not extend I/O operation 21842421Syokota * across more than one interrupt or timeout; they must complete 21942421Syokota * necessary I/O operation within one invokation of the routine. 22042421Syokota * This measns that if the timeout routine acquires the lock flag, 22142421Syokota * it must reset the flag to FALSE before it returns. 22242421Syokota */ 22342421Syokota 22442421Syokota/* set/reset polling lock */ 22542421Syokotaint 22642421Syokotakbdc_lock(KBDC p, int lock) 22742421Syokota{ 22842421Syokota int prevlock; 22942421Syokota 23042421Syokota prevlock = kbdcp(p)->lock; 23142421Syokota kbdcp(p)->lock = lock; 23242421Syokota 23342421Syokota return (prevlock != lock); 23442421Syokota} 23542421Syokota 23642421Syokota/* check if any data is waiting to be processed */ 23742421Syokotaint 23842421Syokotakbdc_data_ready(KBDC p) 23942421Syokota{ 24042421Syokota return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux) 24142421Syokota || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL)); 24242421Syokota} 24342421Syokota 24442421Syokota/* queuing functions */ 24542421Syokota 24642421Syokotastatic int 24742421Syokotaaddq(kqueue *q, int c) 24842421Syokota{ 24942421Syokota if (nextq(q->tail) != q->head) { 25042421Syokota q->q[q->tail] = c; 25142421Syokota q->tail = nextq(q->tail); 25242421Syokota#if KBDIO_DEBUG >= 2 25342421Syokota ++q->call_count; 25442421Syokota ++q->qcount; 25542421Syokota if (q->qcount > q->max_qcount) 25642421Syokota q->max_qcount = q->qcount; 25742421Syokota#endif 25842421Syokota return TRUE; 25942421Syokota } 26042421Syokota return FALSE; 26142421Syokota} 26242421Syokota 26342421Syokotastatic int 26442421Syokotaremoveq(kqueue *q) 26542421Syokota{ 26642421Syokota int c; 26742421Syokota 26842421Syokota if (q->tail != q->head) { 26942421Syokota c = q->q[q->head]; 27042421Syokota q->head = nextq(q->head); 27142421Syokota#if KBDIO_DEBUG >= 2 27242421Syokota --q->qcount; 27342421Syokota#endif 27442421Syokota return c; 27542421Syokota } 27642421Syokota return -1; 27742421Syokota} 27842421Syokota 27942421Syokota/* 28042421Syokota * device I/O routines 28142421Syokota */ 28242421Syokotastatic int 28342421Syokotawait_while_controller_busy(struct atkbdc_softc *kbdc) 28442421Syokota{ 28542421Syokota /* CPU will stay inside the loop for 100msec at most */ 28642421Syokota int retry = 5000; 28742421Syokota int port = kbdc->port; 28842421Syokota int f; 28942421Syokota 29042421Syokota while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) { 29142421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { 29242421Syokota DELAY(KBDD_DELAYTIME); 29342421Syokota addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); 29442421Syokota } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { 29542421Syokota DELAY(KBDD_DELAYTIME); 29642421Syokota addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); 29742421Syokota } 29842421Syokota DELAY(KBDC_DELAYTIME); 29942421Syokota if (--retry < 0) 30042421Syokota return FALSE; 30142421Syokota } 30242421Syokota return TRUE; 30342421Syokota} 30442421Syokota 30542421Syokota/* 30642421Syokota * wait for any data; whether it's from the controller, 30742421Syokota * the keyboard, or the aux device. 30842421Syokota */ 30942421Syokotastatic int 31042421Syokotawait_for_data(struct atkbdc_softc *kbdc) 31142421Syokota{ 31242421Syokota /* CPU will stay inside the loop for 200msec at most */ 31342421Syokota int retry = 10000; 31442421Syokota int port = kbdc->port; 31542421Syokota int f; 31642421Syokota 31742421Syokota while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) { 31842421Syokota DELAY(KBDC_DELAYTIME); 31942421Syokota if (--retry < 0) 32042421Syokota return 0; 32142421Syokota } 32242421Syokota DELAY(KBDD_DELAYTIME); 32342421Syokota return f; 32442421Syokota} 32542421Syokota 32642421Syokota/* wait for data from the keyboard */ 32742421Syokotastatic int 32842421Syokotawait_for_kbd_data(struct atkbdc_softc *kbdc) 32942421Syokota{ 33042421Syokota /* CPU will stay inside the loop for 200msec at most */ 33142421Syokota int retry = 10000; 33242421Syokota int port = kbdc->port; 33342421Syokota int f; 33442421Syokota 33542421Syokota while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) 33642421Syokota != KBDS_KBD_BUFFER_FULL) { 33742421Syokota if (f == KBDS_AUX_BUFFER_FULL) { 33842421Syokota DELAY(KBDD_DELAYTIME); 33942421Syokota addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); 34042421Syokota } 34142421Syokota DELAY(KBDC_DELAYTIME); 34242421Syokota if (--retry < 0) 34342421Syokota return 0; 34442421Syokota } 34542421Syokota DELAY(KBDD_DELAYTIME); 34642421Syokota return f; 34742421Syokota} 34842421Syokota 34942421Syokota/* 35042421Syokota * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard. 35142421Syokota * queue anything else. 35242421Syokota */ 35342421Syokotastatic int 35442421Syokotawait_for_kbd_ack(struct atkbdc_softc *kbdc) 35542421Syokota{ 35642421Syokota /* CPU will stay inside the loop for 200msec at most */ 35742421Syokota int retry = 10000; 35842421Syokota int port = kbdc->port; 35942421Syokota int f; 36042421Syokota int b; 36142421Syokota 36242421Syokota while (retry-- > 0) { 36342421Syokota if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { 36442421Syokota DELAY(KBDD_DELAYTIME); 36542421Syokota b = inb(port + KBD_DATA_PORT); 36642421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { 36742421Syokota if ((b == KBD_ACK) || (b == KBD_RESEND) 36842421Syokota || (b == KBD_RESET_FAIL)) 36942421Syokota return b; 37042421Syokota addq(&kbdc->kbd, b); 37142421Syokota } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { 37242421Syokota addq(&kbdc->aux, b); 37342421Syokota } 37442421Syokota } 37542421Syokota DELAY(KBDC_DELAYTIME); 37642421Syokota } 37742421Syokota return -1; 37842421Syokota} 37942421Syokota 38042421Syokota/* wait for data from the aux device */ 38142421Syokotastatic int 38242421Syokotawait_for_aux_data(struct atkbdc_softc *kbdc) 38342421Syokota{ 38442421Syokota /* CPU will stay inside the loop for 200msec at most */ 38542421Syokota int retry = 10000; 38642421Syokota int port = kbdc->port; 38742421Syokota int f; 38842421Syokota 38942421Syokota while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) 39042421Syokota != KBDS_AUX_BUFFER_FULL) { 39142421Syokota if (f == KBDS_KBD_BUFFER_FULL) { 39242421Syokota DELAY(KBDD_DELAYTIME); 39342421Syokota addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); 39442421Syokota } 39542421Syokota DELAY(KBDC_DELAYTIME); 39642421Syokota if (--retry < 0) 39742421Syokota return 0; 39842421Syokota } 39942421Syokota DELAY(KBDD_DELAYTIME); 40042421Syokota return f; 40142421Syokota} 40242421Syokota 40342421Syokota/* 40442421Syokota * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device. 40542421Syokota * queue anything else. 40642421Syokota */ 40742421Syokotastatic int 40842421Syokotawait_for_aux_ack(struct atkbdc_softc *kbdc) 40942421Syokota{ 41042421Syokota /* CPU will stay inside the loop for 200msec at most */ 41142421Syokota int retry = 10000; 41242421Syokota int port = kbdc->port; 41342421Syokota int f; 41442421Syokota int b; 41542421Syokota 41642421Syokota while (retry-- > 0) { 41742421Syokota if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { 41842421Syokota DELAY(KBDD_DELAYTIME); 41942421Syokota b = inb(port + KBD_DATA_PORT); 42042421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { 42142421Syokota if ((b == PSM_ACK) || (b == PSM_RESEND) 42242421Syokota || (b == PSM_RESET_FAIL)) 42342421Syokota return b; 42442421Syokota addq(&kbdc->aux, b); 42542421Syokota } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { 42642421Syokota addq(&kbdc->kbd, b); 42742421Syokota } 42842421Syokota } 42942421Syokota DELAY(KBDC_DELAYTIME); 43042421Syokota } 43142421Syokota return -1; 43242421Syokota} 43342421Syokota 43442421Syokota/* write a one byte command to the controller */ 43542421Syokotaint 43642421Syokotawrite_controller_command(KBDC p, int c) 43742421Syokota{ 43842421Syokota if (!wait_while_controller_busy(kbdcp(p))) 43942421Syokota return FALSE; 44042421Syokota outb(kbdcp(p)->port + KBD_COMMAND_PORT, c); 44142421Syokota return TRUE; 44242421Syokota} 44342421Syokota 44442421Syokota/* write a one byte data to the controller */ 44542421Syokotaint 44642421Syokotawrite_controller_data(KBDC p, int c) 44742421Syokota{ 44842421Syokota if (!wait_while_controller_busy(kbdcp(p))) 44942421Syokota return FALSE; 45042421Syokota outb(kbdcp(p)->port + KBD_DATA_PORT, c); 45142421Syokota return TRUE; 45242421Syokota} 45342421Syokota 45442421Syokota/* write a one byte keyboard command */ 45542421Syokotaint 45642421Syokotawrite_kbd_command(KBDC p, int c) 45742421Syokota{ 45842421Syokota if (!wait_while_controller_busy(kbdcp(p))) 45942421Syokota return FALSE; 46042421Syokota outb(kbdcp(p)->port + KBD_DATA_PORT, c); 46142421Syokota return TRUE; 46242421Syokota} 46342421Syokota 46442421Syokota/* write a one byte auxiliary device command */ 46542421Syokotaint 46642421Syokotawrite_aux_command(KBDC p, int c) 46742421Syokota{ 46842421Syokota if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) 46942421Syokota return FALSE; 47042421Syokota return write_controller_data(p, c); 47142421Syokota} 47242421Syokota 47342421Syokota/* send a command to the keyboard and wait for ACK */ 47442421Syokotaint 47542421Syokotasend_kbd_command(KBDC p, int c) 47642421Syokota{ 47742421Syokota int retry = KBD_MAXRETRY; 47842421Syokota int res = -1; 47942421Syokota 48042421Syokota while (retry-- > 0) { 48142421Syokota if (!write_kbd_command(p, c)) 48242421Syokota continue; 48342421Syokota res = wait_for_kbd_ack(kbdcp(p)); 48442421Syokota if (res == KBD_ACK) 48542421Syokota break; 48642421Syokota } 48742421Syokota return res; 48842421Syokota} 48942421Syokota 49042421Syokota/* send a command to the auxiliary device and wait for ACK */ 49142421Syokotaint 49242421Syokotasend_aux_command(KBDC p, int c) 49342421Syokota{ 49442421Syokota int retry = KBD_MAXRETRY; 49542421Syokota int res = -1; 49642421Syokota 49742421Syokota while (retry-- > 0) { 49842421Syokota if (!write_aux_command(p, c)) 49942421Syokota continue; 50042421Syokota /* 50142421Syokota * FIXME: XXX 50242421Syokota * The aux device may have already sent one or two bytes of 50342421Syokota * status data, when a command is received. It will immediately 50442421Syokota * stop data transmission, thus, leaving an incomplete data 50542421Syokota * packet in our buffer. We have to discard any unprocessed 50642421Syokota * data in order to remove such packets. Well, we may remove 50742421Syokota * unprocessed, but necessary data byte as well... 50842421Syokota */ 50942421Syokota emptyq(&kbdcp(p)->aux); 51042421Syokota res = wait_for_aux_ack(kbdcp(p)); 51142421Syokota if (res == PSM_ACK) 51242421Syokota break; 51342421Syokota } 51442421Syokota return res; 51542421Syokota} 51642421Syokota 51742421Syokota/* send a command and a data to the keyboard, wait for ACKs */ 51842421Syokotaint 51942421Syokotasend_kbd_command_and_data(KBDC p, int c, int d) 52042421Syokota{ 52142421Syokota int retry; 52242421Syokota int res = -1; 52342421Syokota 52442421Syokota for (retry = KBD_MAXRETRY; retry > 0; --retry) { 52542421Syokota if (!write_kbd_command(p, c)) 52642421Syokota continue; 52742421Syokota res = wait_for_kbd_ack(kbdcp(p)); 52842421Syokota if (res == KBD_ACK) 52942421Syokota break; 53042421Syokota else if (res != KBD_RESEND) 53142421Syokota return res; 53242421Syokota } 53342421Syokota if (retry <= 0) 53442421Syokota return res; 53542421Syokota 53642421Syokota for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { 53742421Syokota if (!write_kbd_command(p, d)) 53842421Syokota continue; 53942421Syokota res = wait_for_kbd_ack(kbdcp(p)); 54042421Syokota if (res != KBD_RESEND) 54142421Syokota break; 54242421Syokota } 54342421Syokota return res; 54442421Syokota} 54542421Syokota 54642421Syokota/* send a command and a data to the auxiliary device, wait for ACKs */ 54742421Syokotaint 54842421Syokotasend_aux_command_and_data(KBDC p, int c, int d) 54942421Syokota{ 55042421Syokota int retry; 55142421Syokota int res = -1; 55242421Syokota 55342421Syokota for (retry = KBD_MAXRETRY; retry > 0; --retry) { 55442421Syokota if (!write_aux_command(p, c)) 55542421Syokota continue; 55642421Syokota emptyq(&kbdcp(p)->aux); 55742421Syokota res = wait_for_aux_ack(kbdcp(p)); 55842421Syokota if (res == PSM_ACK) 55942421Syokota break; 56042421Syokota else if (res != PSM_RESEND) 56142421Syokota return res; 56242421Syokota } 56342421Syokota if (retry <= 0) 56442421Syokota return res; 56542421Syokota 56642421Syokota for (retry = KBD_MAXRETRY, res = -1; retry > 0; --retry) { 56742421Syokota if (!write_aux_command(p, d)) 56842421Syokota continue; 56942421Syokota res = wait_for_aux_ack(kbdcp(p)); 57042421Syokota if (res != PSM_RESEND) 57142421Syokota break; 57242421Syokota } 57342421Syokota return res; 57442421Syokota} 57542421Syokota 57642421Syokota/* 57742421Syokota * read one byte from any source; whether from the controller, 57842421Syokota * the keyboard, or the aux device 57942421Syokota */ 58042421Syokotaint 58142421Syokotaread_controller_data(KBDC p) 58242421Syokota{ 58342421Syokota if (availq(&kbdcp(p)->kbd)) 58442421Syokota return removeq(&kbdcp(p)->kbd); 58542421Syokota if (availq(&kbdcp(p)->aux)) 58642421Syokota return removeq(&kbdcp(p)->aux); 58742421Syokota if (!wait_for_data(kbdcp(p))) 58842421Syokota return -1; /* timeout */ 58942421Syokota return inb(kbdcp(p)->port + KBD_DATA_PORT); 59042421Syokota} 59142421Syokota 59242421Syokota#if KBDIO_DEBUG >= 2 59342421Syokotastatic int call = 0; 59442421Syokota#endif 59542421Syokota 59642421Syokota/* read one byte from the keyboard */ 59742421Syokotaint 59842421Syokotaread_kbd_data(KBDC p) 59942421Syokota{ 60042421Syokota#if KBDIO_DEBUG >= 2 60142421Syokota if (++call > 2000) { 60242421Syokota call = 0; 60342421Syokota log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " 60442421Syokota "aux q: %d calls, max %d chars\n", 60542421Syokota kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, 60642421Syokota kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); 60742421Syokota } 60842421Syokota#endif 60942421Syokota 61042421Syokota if (availq(&kbdcp(p)->kbd)) 61142421Syokota return removeq(&kbdcp(p)->kbd); 61242421Syokota if (!wait_for_kbd_data(kbdcp(p))) 61342421Syokota return -1; /* timeout */ 61442421Syokota return inb(kbdcp(p)->port + KBD_DATA_PORT); 61542421Syokota} 61642421Syokota 61742421Syokota/* read one byte from the keyboard, but return immediately if 61842421Syokota * no data is waiting 61942421Syokota */ 62042421Syokotaint 62142421Syokotaread_kbd_data_no_wait(KBDC p) 62242421Syokota{ 62342421Syokota int f; 62442421Syokota 62542421Syokota#if KBDIO_DEBUG >= 2 62642421Syokota if (++call > 2000) { 62742421Syokota call = 0; 62842421Syokota log(LOG_DEBUG, "kbdc: kbd q: %d calls, max %d chars, " 62942421Syokota "aux q: %d calls, max %d chars\n", 63042421Syokota kbdcp(p)->kbd.call_count, kbdcp(p)->kbd.max_qcount, 63142421Syokota kbdcp(p)->aux.call_count, kbdcp(p)->aux.max_qcount); 63242421Syokota } 63342421Syokota#endif 63442421Syokota 63542421Syokota if (availq(&kbdcp(p)->kbd)) 63642421Syokota return removeq(&kbdcp(p)->kbd); 63742421Syokota f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; 63842421Syokota if (f == KBDS_AUX_BUFFER_FULL) { 63942421Syokota DELAY(KBDD_DELAYTIME); 64042421Syokota addq(&kbdcp(p)->aux, inb(kbdcp(p)->port + KBD_DATA_PORT)); 64142421Syokota f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; 64242421Syokota } 64342421Syokota if (f == KBDS_KBD_BUFFER_FULL) { 64442421Syokota DELAY(KBDD_DELAYTIME); 64542421Syokota return inb(kbdcp(p)->port + KBD_DATA_PORT); 64642421Syokota } 64742421Syokota return -1; /* no data */ 64842421Syokota} 64942421Syokota 65042421Syokota/* read one byte from the aux device */ 65142421Syokotaint 65242421Syokotaread_aux_data(KBDC p) 65342421Syokota{ 65442421Syokota if (availq(&kbdcp(p)->aux)) 65542421Syokota return removeq(&kbdcp(p)->aux); 65642421Syokota if (!wait_for_aux_data(kbdcp(p))) 65742421Syokota return -1; /* timeout */ 65842421Syokota return inb(kbdcp(p)->port + KBD_DATA_PORT); 65942421Syokota} 66042421Syokota 66142421Syokota/* read one byte from the aux device, but return immediately if 66242421Syokota * no data is waiting 66342421Syokota */ 66442421Syokotaint 66542421Syokotaread_aux_data_no_wait(KBDC p) 66642421Syokota{ 66742421Syokota int f; 66842421Syokota 66942421Syokota if (availq(&kbdcp(p)->aux)) 67042421Syokota return removeq(&kbdcp(p)->aux); 67142421Syokota f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; 67242421Syokota if (f == KBDS_KBD_BUFFER_FULL) { 67342421Syokota DELAY(KBDD_DELAYTIME); 67442421Syokota addq(&kbdcp(p)->kbd, inb(kbdcp(p)->port + KBD_DATA_PORT)); 67542421Syokota f = inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL; 67642421Syokota } 67742421Syokota if (f == KBDS_AUX_BUFFER_FULL) { 67842421Syokota DELAY(KBDD_DELAYTIME); 67942421Syokota return inb(kbdcp(p)->port + KBD_DATA_PORT); 68042421Syokota } 68142421Syokota return -1; /* no data */ 68242421Syokota} 68342421Syokota 68442421Syokota/* discard data from the keyboard */ 68542421Syokotavoid 68642421Syokotaempty_kbd_buffer(KBDC p, int wait) 68742421Syokota{ 68842421Syokota int t; 68942421Syokota int b; 69042421Syokota int f; 69142421Syokota#if KBDIO_DEBUG >= 2 69242421Syokota int c1 = 0; 69342421Syokota int c2 = 0; 69442421Syokota#endif 69542421Syokota int delta = 2; 69642421Syokota 69742421Syokota for (t = wait; t > 0; ) { 69842421Syokota if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { 69942421Syokota DELAY(KBDD_DELAYTIME); 70042421Syokota b = inb(kbdcp(p)->port + KBD_DATA_PORT); 70142421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { 70242421Syokota addq(&kbdcp(p)->aux, b); 70342421Syokota#if KBDIO_DEBUG >= 2 70442421Syokota ++c2; 70542421Syokota } else { 70642421Syokota ++c1; 70742421Syokota#endif 70842421Syokota } 70942421Syokota t = wait; 71042421Syokota } else { 71142421Syokota t -= delta; 71242421Syokota } 71342421Syokota DELAY(delta*1000); 71442421Syokota } 71542421Syokota#if KBDIO_DEBUG >= 2 71642421Syokota if ((c1 > 0) || (c2 > 0)) 71742421Syokota log(LOG_DEBUG, "kbdc: %d:%d char read (empty_kbd_buffer)\n", c1, c2); 71842421Syokota#endif 71942421Syokota 72042421Syokota emptyq(&kbdcp(p)->kbd); 72142421Syokota} 72242421Syokota 72342421Syokota/* discard data from the aux device */ 72442421Syokotavoid 72542421Syokotaempty_aux_buffer(KBDC p, int wait) 72642421Syokota{ 72742421Syokota int t; 72842421Syokota int b; 72942421Syokota int f; 73042421Syokota#if KBDIO_DEBUG >= 2 73142421Syokota int c1 = 0; 73242421Syokota int c2 = 0; 73342421Syokota#endif 73442421Syokota int delta = 2; 73542421Syokota 73642421Syokota for (t = wait; t > 0; ) { 73742421Syokota if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { 73842421Syokota DELAY(KBDD_DELAYTIME); 73942421Syokota b = inb(kbdcp(p)->port + KBD_DATA_PORT); 74042421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { 74142421Syokota addq(&kbdcp(p)->kbd, b); 74242421Syokota#if KBDIO_DEBUG >= 2 74342421Syokota ++c1; 74442421Syokota } else { 74542421Syokota ++c2; 74642421Syokota#endif 74742421Syokota } 74842421Syokota t = wait; 74942421Syokota } else { 75042421Syokota t -= delta; 75142421Syokota } 75242421Syokota DELAY(delta*1000); 75342421Syokota } 75442421Syokota#if KBDIO_DEBUG >= 2 75542421Syokota if ((c1 > 0) || (c2 > 0)) 75642421Syokota log(LOG_DEBUG, "kbdc: %d:%d char read (empty_aux_buffer)\n", c1, c2); 75742421Syokota#endif 75842421Syokota 75942421Syokota emptyq(&kbdcp(p)->aux); 76042421Syokota} 76142421Syokota 76242421Syokota/* discard any data from the keyboard or the aux device */ 76342421Syokotavoid 76442421Syokotaempty_both_buffers(KBDC p, int wait) 76542421Syokota{ 76642421Syokota int t; 76742421Syokota int f; 76842421Syokota#if KBDIO_DEBUG >= 2 76942421Syokota int c1 = 0; 77042421Syokota int c2 = 0; 77142421Syokota#endif 77242421Syokota int delta = 2; 77342421Syokota 77442421Syokota for (t = wait; t > 0; ) { 77542421Syokota if ((f = inb(kbdcp(p)->port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { 77642421Syokota DELAY(KBDD_DELAYTIME); 77742421Syokota (void)inb(kbdcp(p)->port + KBD_DATA_PORT); 77842421Syokota#if KBDIO_DEBUG >= 2 77942421Syokota if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) 78042421Syokota ++c1; 78142421Syokota else 78242421Syokota ++c2; 78342421Syokota#endif 78442421Syokota t = wait; 78542421Syokota } else { 78642421Syokota t -= delta; 78742421Syokota } 78842421Syokota DELAY(delta*1000); 78942421Syokota } 79042421Syokota#if KBDIO_DEBUG >= 2 79142421Syokota if ((c1 > 0) || (c2 > 0)) 79242421Syokota log(LOG_DEBUG, "kbdc: %d:%d char read (empty_both_buffers)\n", c1, c2); 79342421Syokota#endif 79442421Syokota 79542421Syokota emptyq(&kbdcp(p)->kbd); 79642421Syokota emptyq(&kbdcp(p)->aux); 79742421Syokota} 79842421Syokota 79942421Syokota/* keyboard and mouse device control */ 80042421Syokota 80142421Syokota/* NOTE: enable the keyboard port but disable the keyboard 80242421Syokota * interrupt before calling "reset_kbd()". 80342421Syokota */ 80442421Syokotaint 80542421Syokotareset_kbd(KBDC p) 80642421Syokota{ 80742421Syokota int retry = KBD_MAXRETRY; 80842421Syokota int again = KBD_MAXWAIT; 80942421Syokota int c = KBD_RESEND; /* keep the compiler happy */ 81042421Syokota 81142421Syokota while (retry-- > 0) { 81242421Syokota empty_both_buffers(p, 10); 81342421Syokota if (!write_kbd_command(p, KBDC_RESET_KBD)) 81442421Syokota continue; 81542421Syokota emptyq(&kbdcp(p)->kbd); 81642421Syokota c = read_controller_data(p); 81742421Syokota if (verbose || bootverbose) 81842421Syokota log(LOG_DEBUG, "kbdc: RESET_KBD return code:%04x\n", c); 81942421Syokota if (c == KBD_ACK) /* keyboard has agreed to reset itself... */ 82042421Syokota break; 82142421Syokota } 82242421Syokota if (retry < 0) 82342421Syokota return FALSE; 82442421Syokota 82542421Syokota while (again-- > 0) { 82642421Syokota /* wait awhile, well, in fact we must wait quite loooooooooooong */ 82742421Syokota DELAY(KBD_RESETDELAY*1000); 82842421Syokota c = read_controller_data(p); /* RESET_DONE/RESET_FAIL */ 82942421Syokota if (c != -1) /* wait again if the controller is not ready */ 83042421Syokota break; 83142421Syokota } 83242421Syokota if (verbose || bootverbose) 83342421Syokota log(LOG_DEBUG, "kbdc: RESET_KBD status:%04x\n", c); 83442421Syokota if (c != KBD_RESET_DONE) 83542421Syokota return FALSE; 83642421Syokota return TRUE; 83742421Syokota} 83842421Syokota 83942421Syokota/* NOTE: enable the aux port but disable the aux interrupt 84042421Syokota * before calling `reset_aux_dev()'. 84142421Syokota */ 84242421Syokotaint 84342421Syokotareset_aux_dev(KBDC p) 84442421Syokota{ 84542421Syokota int retry = KBD_MAXRETRY; 84642421Syokota int again = KBD_MAXWAIT; 84742421Syokota int c = PSM_RESEND; /* keep the compiler happy */ 84842421Syokota 84942421Syokota while (retry-- > 0) { 85042421Syokota empty_both_buffers(p, 10); 85142421Syokota if (!write_aux_command(p, PSMC_RESET_DEV)) 85242421Syokota continue; 85342421Syokota emptyq(&kbdcp(p)->aux); 85442421Syokota /* NOTE: Compaq Armada laptops require extra delay here. XXX */ 85542421Syokota for (again = KBD_MAXWAIT; again > 0; --again) { 85642421Syokota DELAY(KBD_RESETDELAY*1000); 85742421Syokota c = read_aux_data_no_wait(p); 85842421Syokota if (c != -1) 85942421Syokota break; 86042421Syokota } 86142421Syokota if (verbose || bootverbose) 86242421Syokota log(LOG_DEBUG, "kbdc: RESET_AUX return code:%04x\n", c); 86342421Syokota if (c == PSM_ACK) /* aux dev is about to reset... */ 86442421Syokota break; 86542421Syokota } 86642421Syokota if (retry < 0) 86742421Syokota return FALSE; 86842421Syokota 86942421Syokota for (again = KBD_MAXWAIT; again > 0; --again) { 87042421Syokota /* wait awhile, well, quite looooooooooooong */ 87142421Syokota DELAY(KBD_RESETDELAY*1000); 87242421Syokota c = read_aux_data_no_wait(p); /* RESET_DONE/RESET_FAIL */ 87342421Syokota if (c != -1) /* wait again if the controller is not ready */ 87442421Syokota break; 87542421Syokota } 87642421Syokota if (verbose || bootverbose) 87742421Syokota log(LOG_DEBUG, "kbdc: RESET_AUX status:%04x\n", c); 87842421Syokota if (c != PSM_RESET_DONE) /* reset status */ 87942421Syokota return FALSE; 88042421Syokota 88142421Syokota c = read_aux_data(p); /* device ID */ 88242421Syokota if (verbose || bootverbose) 88342421Syokota log(LOG_DEBUG, "kbdc: RESET_AUX ID:%04x\n", c); 88442421Syokota /* NOTE: we could check the device ID now, but leave it later... */ 88542421Syokota return TRUE; 88642421Syokota} 88742421Syokota 88842421Syokota/* controller diagnostics and setup */ 88942421Syokota 89042421Syokotaint 89142421Syokotatest_controller(KBDC p) 89242421Syokota{ 89342421Syokota int retry = KBD_MAXRETRY; 89442421Syokota int again = KBD_MAXWAIT; 89542421Syokota int c = KBD_DIAG_FAIL; 89642421Syokota 89742421Syokota while (retry-- > 0) { 89842421Syokota empty_both_buffers(p, 10); 89942421Syokota if (write_controller_command(p, KBDC_DIAGNOSE)) 90042421Syokota break; 90142421Syokota } 90242421Syokota if (retry < 0) 90342421Syokota return FALSE; 90442421Syokota 90542421Syokota emptyq(&kbdcp(p)->kbd); 90642421Syokota while (again-- > 0) { 90742421Syokota /* wait awhile */ 90842421Syokota DELAY(KBD_RESETDELAY*1000); 90942421Syokota c = read_controller_data(p); /* DIAG_DONE/DIAG_FAIL */ 91042421Syokota if (c != -1) /* wait again if the controller is not ready */ 91142421Syokota break; 91242421Syokota } 91342421Syokota if (verbose || bootverbose) 91442421Syokota log(LOG_DEBUG, "kbdc: DIAGNOSE status:%04x\n", c); 91542421Syokota return (c == KBD_DIAG_DONE); 91642421Syokota} 91742421Syokota 91842421Syokotaint 91942421Syokotatest_kbd_port(KBDC p) 92042421Syokota{ 92142421Syokota int retry = KBD_MAXRETRY; 92242421Syokota int again = KBD_MAXWAIT; 92342421Syokota int c = -1; 92442421Syokota 92542421Syokota while (retry-- > 0) { 92642421Syokota empty_both_buffers(p, 10); 92742421Syokota if (write_controller_command(p, KBDC_TEST_KBD_PORT)) 92842421Syokota break; 92942421Syokota } 93042421Syokota if (retry < 0) 93142421Syokota return FALSE; 93242421Syokota 93342421Syokota emptyq(&kbdcp(p)->kbd); 93442421Syokota while (again-- > 0) { 93542421Syokota c = read_controller_data(p); 93642421Syokota if (c != -1) /* try again if the controller is not ready */ 93742421Syokota break; 93842421Syokota } 93942421Syokota if (verbose || bootverbose) 94042421Syokota log(LOG_DEBUG, "kbdc: TEST_KBD_PORT status:%04x\n", c); 94142421Syokota return c; 94242421Syokota} 94342421Syokota 94442421Syokotaint 94542421Syokotatest_aux_port(KBDC p) 94642421Syokota{ 94742421Syokota int retry = KBD_MAXRETRY; 94842421Syokota int again = KBD_MAXWAIT; 94942421Syokota int c = -1; 95042421Syokota 95142421Syokota while (retry-- > 0) { 95242421Syokota empty_both_buffers(p, 10); 95342421Syokota if (write_controller_command(p, KBDC_TEST_AUX_PORT)) 95442421Syokota break; 95542421Syokota } 95642421Syokota if (retry < 0) 95742421Syokota return FALSE; 95842421Syokota 95942421Syokota emptyq(&kbdcp(p)->kbd); 96042421Syokota while (again-- > 0) { 96142421Syokota c = read_controller_data(p); 96242421Syokota if (c != -1) /* try again if the controller is not ready */ 96342421Syokota break; 96442421Syokota } 96542421Syokota if (verbose || bootverbose) 96642421Syokota log(LOG_DEBUG, "kbdc: TEST_AUX_PORT status:%04x\n", c); 96742421Syokota return c; 96842421Syokota} 96942421Syokota 97042421Syokotaint 97142421Syokotakbdc_get_device_mask(KBDC p) 97242421Syokota{ 97342421Syokota return kbdcp(p)->command_mask; 97442421Syokota} 97542421Syokota 97642421Syokotavoid 97742421Syokotakbdc_set_device_mask(KBDC p, int mask) 97842421Syokota{ 97942421Syokota kbdcp(p)->command_mask = 98042421Syokota mask & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS); 98142421Syokota} 98242421Syokota 98342421Syokotaint 98442421Syokotaget_controller_command_byte(KBDC p) 98542421Syokota{ 98642421Syokota if (kbdcp(p)->command_byte != -1) 98742421Syokota return kbdcp(p)->command_byte; 98842421Syokota if (!write_controller_command(p, KBDC_GET_COMMAND_BYTE)) 98942421Syokota return -1; 99042421Syokota emptyq(&kbdcp(p)->kbd); 99142421Syokota kbdcp(p)->command_byte = read_controller_data(p); 99242421Syokota return kbdcp(p)->command_byte; 99342421Syokota} 99442421Syokota 99542421Syokotaint 99642421Syokotaset_controller_command_byte(KBDC p, int mask, int command) 99742421Syokota{ 99842421Syokota if (get_controller_command_byte(p) == -1) 99942421Syokota return FALSE; 100042421Syokota 100142421Syokota command = (kbdcp(p)->command_byte & ~mask) | (command & mask); 100242421Syokota if (command & KBD_DISABLE_KBD_PORT) { 100342421Syokota if (!write_controller_command(p, KBDC_DISABLE_KBD_PORT)) 100442421Syokota return FALSE; 100542421Syokota } 100642421Syokota if (!write_controller_command(p, KBDC_SET_COMMAND_BYTE)) 100742421Syokota return FALSE; 100842421Syokota if (!write_controller_data(p, command)) 100942421Syokota return FALSE; 101042421Syokota kbdcp(p)->command_byte = command; 101142421Syokota 101242421Syokota if (verbose) 101342421Syokota log(LOG_DEBUG, "kbdc: new command byte:%04x (set_controller...)\n", 101442421Syokota command); 101542421Syokota 101642421Syokota return TRUE; 101742421Syokota} 1018