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