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 + -
显示快捷键?