/*- * Copyright (c) 1996 Shin Takemura All rights reserved. * * This code is derived from i386/isa/kdbio.c. * * 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: kbdio.c,v 1.1.4.1 1998/12/05 22:49:12 itojun Exp $ */ #ifdef NECMG #include "sc.h" #include "vt.h" #include "psm.h" #include "opt_kbdio.h" #include #include #include #include #include #include #include /* I/O recovery time */ #define KBDC_DELAYTIME 20 #define KBDD_DELAYTIME 7 #ifndef max #define max(x,y) ((x) > (y) ? (x) : (y)) #endif #define kbdcp(p) ((struct kbdc_softc *)(p)) #define NKBDC max(NSC, NVT) struct kbdc_softc { int port; /* base port address */ int lock; /* FIXME: XXX not quite a semaphore... */ }; static struct kbdc_softc kbdc_softc[NKBDC] = { { 0 }, }; /* * associate a port number with a KBDC */ KBDC kbdc_open(int port) { int s; int i; s = spltty(); for (i = 0; i < NKBDC; ++i) { if (kbdc_softc[i].port == port) { splx(s); return (KBDC) &kbdc_softc[i]; } if (kbdc_softc[i].port <= 0) { kbdc_softc[i].port = port; kbdc_softc[i].lock = FALSE; splx(s); return (KBDC) &kbdc_softc[i]; } } splx(s); return NULL; } /* * I/O access arbitration in `kbdio' * * The `kbdio' module uses a simplistic convention to arbitrate * I/O access to the controller/keyboard/mouse. The convention requires * close cooperation of the calling device driver. * * The device driver which utilizes the `kbdio' module are assumed to * have the following set of routines. * a. An interrupt handler (the bottom half of the driver). * b. Timeout routines which may briefly polls the keyboard controller. * c. Routines outside interrupt context (the top half of the driver). * They should follow the rules below: * 1. The interrupt handler may assume that it always has full access * to the controller/keyboard/mouse. * 2. The other routines must issue `spltty()' if they wish to * prevent the interrupt handler from accessing * the controller/keyboard/mouse. * 3. The timeout routines and the top half routines of the device driver * arbitrate I/O access by observing the lock flag in `kbdio'. * The flag is manipulated via `kbdc_lock()'; when one wants to * perform I/O, call `kbdc_lock(kbdc, TRUE)' and proceed only if * the call returns with TRUE. Otherwise the caller must back off. * Call `kbdc_lock(kbdc, FALSE)' when necessary I/O operaion * is finished. This mechanism does not prevent the interrupt * handler from being invoked at any time and carrying out I/O. * Therefore, `spltty()' must be strategically placed in the device * driver code. Also note that the timeout routine may interrupt * `kbdc_lock()' called by the top half of the driver, but this * interruption is OK so long as the timeout routine observes the * the rule 4 below. * 4. The interrupt and timeout routines should not extend I/O operation * across more than one interrupt or timeout; they must complete * necessary I/O operation within one invokation of the routine. * This measns that if the timeout routine acquires the lock flag, * it must reset the flag to FALSE before it returns. */ /* set/reset polling lock */ int kbdc_lock(KBDC p, int lock) { int prevlock; prevlock = kbdcp(p)->lock; kbdcp(p)->lock = lock; return (prevlock != lock); } /* check if any data is waiting to be processed */ int kbdc_data_ready(KBDC p) { return (inb(kbdcp(p)->port + KBD_STATUS_PORT) & 0x40); } static void read_kbd_data_done(struct kbdc_softc *kbdc) { unsigned char d; d = inb(kbdc->port + KBD_STATUS_PORT); outb(kbdc->port + KBD_STATUS_PORT, (d | 0x80)); DELAY(KBDC_DELAYTIME); outb(kbdc->port + KBD_STATUS_PORT, d); DELAY(KBDC_DELAYTIME); } /* read one byte from the keyboard */ int read_kbd_data(KBDC p) { /* CPU will stay inside the loop for 200msec at most */ struct kbdc_softc *kbdc = kbdcp(p); int retry = 10000; int port = kbdc->port; int d; while ((d = inb(port + KBD_DATA_PORT)) == 0) { DELAY(KBDD_DELAYTIME); if (--retry < 0) return -1; /* timeout */ } read_kbd_data_done(kbdc); return (d); } /* read one byte from the keyboard, but return immediately if * no data is waiting */ int read_kbd_data_no_wait(KBDC p) { int c; c = inb(kbdcp(p)->port + KBD_DATA_PORT); if (c == 0) { return (-1); /* no data */ } else { read_kbd_data_done(kbdcp(p)); return (c); } } #endif /* NECMG */