atkbdc.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,018 行 · 第 1/2 页
C
1,018 行
/*- * Copyright (c) 1996-1999 * 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 AUTHOR 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 AUTHOR 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: atkbdc.c,v 1.1 1999/01/09 02:44:50 yokota Exp $ * from kbdio.c,v 1.13 1998/09/25 11:55:46 yokota Exp */#include "atkbdc.h"#include "opt_kbd.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.h>#include <sys/malloc.h>#include <sys/syslog.h>#include <machine/clock.h>#include <dev/kbd/atkbdcreg.h>#ifndef __i386__#include <isa/isareg.h>#else#include <i386/isa/isa.h>#endif/* constants */#define MAXKBDC MAX(NATKBDC, 1)/* macros */#ifndef MAX#define MAX(x, y) ((x) > (y) ? (x) : (y))#endif#define kbdcp(p) ((atkbdc_softc_t *)(p))#define nextq(i) (((i) + 1) % KBDQ_BUFSIZE)#define availq(q) ((q)->head != (q)->tail)#if KBDIO_DEBUG >= 2#define emptyq(q) ((q)->tail = (q)->head = (q)->qcount = 0)#else#define emptyq(q) ((q)->tail = (q)->head = 0)#endif/* local variables *//* * We always need at least one copy of the kbdc_softc struct for the * low-level console. As the low-level console accesses the keyboard * controller before kbdc, and all other devices, is probed, we * statically allocate one entry. XXX */static atkbdc_softc_t default_kbdc;static atkbdc_softc_t *atkbdc_softc[MAXKBDC] = { &default_kbdc };static int verbose = KBDIO_DEBUG;/* function prototypes */static int atkbdc_setup(atkbdc_softc_t *sc, int port);static int addq(kqueue *q, int c);static int removeq(kqueue *q);static int wait_while_controller_busy(atkbdc_softc_t *kbdc);static int wait_for_data(atkbdc_softc_t *kbdc);static int wait_for_kbd_data(atkbdc_softc_t *kbdc);static int wait_for_kbd_ack(atkbdc_softc_t *kbdc);static int wait_for_aux_data(atkbdc_softc_t *kbdc);static int wait_for_aux_ack(atkbdc_softc_t *kbdc);#if NATKBDC > 0atkbdc_softc_t*atkbdc_get_softc(int unit){ atkbdc_softc_t *sc; if (unit >= sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0])) return NULL; sc = atkbdc_softc[unit]; if (sc == NULL) { sc = atkbdc_softc[unit] = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT); if (sc == NULL) return NULL; bzero(sc, sizeof(*sc)); sc->port = -1; /* XXX */ } return sc;}intatkbdc_probe_unit(atkbdc_softc_t *sc, int unit, int port){ return atkbdc_setup(sc, port);}#endif /* NATKBDC > 0 *//* the backdoor to the keyboard controller! XXX */intatkbdc_configure(void){ return atkbdc_setup(atkbdc_softc[0], -1);}static intatkbdc_setup(atkbdc_softc_t *sc, int port){ if (port <= 0) port = IO_KBD; if (sc->port <= 0) { sc->command_byte = -1; sc->command_mask = 0; sc->lock = FALSE; sc->kbd.head = sc->kbd.tail = 0; sc->aux.head = sc->aux.tail = 0;#if KBDIO_DEBUG >= 2 sc->kbd.call_count = 0; sc->kbd.qcount = sc->kbd.max_qcount = 0; sc->aux.call_count = 0; sc->aux.qcount = sc->aux.max_qcount = 0;#endif } sc->port = port; /* may override the previous value */ return 0;}/* associate a port number with a KBDC */KBDC kbdc_open(int port){ int s; int i; if (port <= 0) port = IO_KBD; s = spltty(); for (i = 0; i < sizeof(atkbdc_softc)/sizeof(atkbdc_softc[0]); ++i) { if (atkbdc_softc[i] == NULL) continue; if (atkbdc_softc[i]->port == port) { splx(s); return (KBDC)atkbdc_softc[i]; } if (atkbdc_softc[i]->port <= 0) { if (atkbdc_setup(atkbdc_softc[i], port)) break; splx(s); return (KBDC)atkbdc_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 */intkbdc_data_ready(KBDC p){ return (availq(&kbdcp(p)->kbd) || availq(&kbdcp(p)->aux) || (inb(kbdcp(p)->port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL));}/* queuing functions */static intaddq(kqueue *q, int c){ if (nextq(q->tail) != q->head) { q->q[q->tail] = c; q->tail = nextq(q->tail);#if KBDIO_DEBUG >= 2 ++q->call_count; ++q->qcount; if (q->qcount > q->max_qcount) q->max_qcount = q->qcount;#endif return TRUE; } return FALSE;}static intremoveq(kqueue *q){ int c; if (q->tail != q->head) { c = q->q[q->head]; q->head = nextq(q->head);#if KBDIO_DEBUG >= 2 --q->qcount;#endif return c; } return -1;}/* * device I/O routines */static intwait_while_controller_busy(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 100msec at most */ int retry = 5000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT)) & KBDS_INPUT_BUFFER_FULL) { if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return FALSE; } return TRUE;}/* * wait for any data; whether it's from the controller, * the keyboard, or the aux device. */static intwait_for_data(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_ANY_BUFFER_FULL) == 0) { DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f;}/* wait for data from the keyboard */static intwait_for_kbd_data(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_KBD_BUFFER_FULL) { if (f == KBDS_AUX_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->aux, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f;}/* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the keyboard. * queue anything else. */static intwait_for_kbd_ack(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { if ((b == KBD_ACK) || (b == KBD_RESEND) || (b == KBD_RESET_FAIL)) return b; addq(&kbdc->kbd, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { addq(&kbdc->aux, b); } } DELAY(KBDC_DELAYTIME); } return -1;}/* wait for data from the aux device */static intwait_for_aux_data(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; while ((f = inb(port + KBD_STATUS_PORT) & KBDS_BUFFER_FULL) != KBDS_AUX_BUFFER_FULL) { if (f == KBDS_KBD_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); addq(&kbdc->kbd, inb(port + KBD_DATA_PORT)); } DELAY(KBDC_DELAYTIME); if (--retry < 0) return 0; } DELAY(KBDD_DELAYTIME); return f;}/* * wait for an ACK(FAh), RESEND(FEh), or RESET_FAIL(FCh) from the aux device. * queue anything else. */static intwait_for_aux_ack(struct atkbdc_softc *kbdc){ /* CPU will stay inside the loop for 200msec at most */ int retry = 10000; int port = kbdc->port; int f; int b; while (retry-- > 0) { if ((f = inb(port + KBD_STATUS_PORT)) & KBDS_ANY_BUFFER_FULL) { DELAY(KBDD_DELAYTIME); b = inb(port + KBD_DATA_PORT); if ((f & KBDS_BUFFER_FULL) == KBDS_AUX_BUFFER_FULL) { if ((b == PSM_ACK) || (b == PSM_RESEND) || (b == PSM_RESET_FAIL)) return b; addq(&kbdc->aux, b); } else if ((f & KBDS_BUFFER_FULL) == KBDS_KBD_BUFFER_FULL) { addq(&kbdc->kbd, b); } } DELAY(KBDC_DELAYTIME); } return -1;}/* write a one byte command to the controller */intwrite_controller_command(KBDC p, int c){ if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_COMMAND_PORT, c); return TRUE;}/* write a one byte data to the controller */intwrite_controller_data(KBDC p, int c){ if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE;}/* write a one byte keyboard command */intwrite_kbd_command(KBDC p, int c){ if (!wait_while_controller_busy(kbdcp(p))) return FALSE; outb(kbdcp(p)->port + KBD_DATA_PORT, c); return TRUE;}/* write a one byte auxiliary device command */intwrite_aux_command(KBDC p, int c){ if (!write_controller_command(p, KBDC_WRITE_TO_AUX)) return FALSE; return write_controller_data(p, c);}/* send a command to the keyboard and wait for ACK */intsend_kbd_command(KBDC p, int c){ int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_kbd_command(p, c)) continue; res = wait_for_kbd_ack(kbdcp(p)); if (res == KBD_ACK) break; } return res;}/* send a command to the auxiliary device and wait for ACK */intsend_aux_command(KBDC p, int c){ int retry = KBD_MAXRETRY; int res = -1; while (retry-- > 0) { if (!write_aux_command(p, c)) continue; /* * FIXME: XXX * The aux device may have already sent one or two bytes of * status data, when a command is received. It will immediately * stop data transmission, thus, leaving an incomplete data * packet in our buffer. We have to discard any unprocessed * data in order to remove such packets. Well, we may remove * unprocessed, but necessary data byte as well... */ emptyq(&kbdcp(p)->aux);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?