338 lines
8.9 KiB
C
338 lines
8.9 KiB
C
/* $NetBSD: kbd.c,v 1.3 1996/11/13 21:13:39 cgd Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1992, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This software was developed by the Computer Systems Engineering group
|
|
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
|
|
* contributed to Berkeley.
|
|
*
|
|
* All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Lawrence Berkeley Laboratory.
|
|
*
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may 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.
|
|
*
|
|
* @(#)kbd.c 8.2 (Berkeley) 10/30/93
|
|
*/
|
|
|
|
/*
|
|
* Keyboard driver (/dev/kbd -- note that we do not have minor numbers
|
|
* [yet?]). Translates incoming bytes to ASCII or to `firm_events' and
|
|
* passes them up to the appropriate reader.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/device.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/tty.h>
|
|
#include <sys/signalvar.h>
|
|
|
|
#include <machine/autoconf.h>
|
|
|
|
#include <machine/vuid_event.h>
|
|
#include <machine/kbio.h> /* XXX FOR KIOCSDIRECT */
|
|
#include <machine/wsconsio.h> /* XXX for bell ioctls */
|
|
#include <alpha/wscons/event_var.h>
|
|
#include <alpha/wscons/wsconsvar.h>
|
|
|
|
static void kbd_repeat __P((void *));
|
|
|
|
cdev_decl(kbd);
|
|
|
|
struct kbd_softc {
|
|
struct device *k_idev; /* the input device */
|
|
struct wscons_idev_spec k_ispec; /* the input device information */
|
|
|
|
int k_evmode; /* set if we should produce events */
|
|
struct evvar k_events; /* event queue state */
|
|
char *k_repeatcp; /* repeated character (string) */
|
|
int k_repeating; /* we've called timeout() */
|
|
int k_repeat_start; /* how long (ms) until repeat */
|
|
int k_repeat_step; /* how long (ms) until more repeats */
|
|
|
|
struct wsconsio_bell_data k_belldata;
|
|
} kbd_softc;
|
|
|
|
void
|
|
kbdattach(idev, ispec)
|
|
struct device *idev;
|
|
struct wscons_idev_spec *ispec;
|
|
{
|
|
register struct kbd_softc *k = &kbd_softc;
|
|
|
|
/*
|
|
* It would be nice if the repeat rates were in ticks.
|
|
* However, if they were, we couldn't set them here, as
|
|
* hz might not be set up yet!
|
|
*/
|
|
k->k_repeat_start = 200;
|
|
k->k_repeat_step = 50;
|
|
|
|
k->k_belldata.wbd_pitch = 1500; /* 1500 Hz */
|
|
k->k_belldata.wbd_period = 100; /* 100 ms */
|
|
k->k_belldata.wbd_volume = 50; /* 50% volume */
|
|
|
|
k->k_idev = idev;
|
|
k->k_ispec = *ispec;
|
|
}
|
|
|
|
static void
|
|
kbd_repeat(void *arg)
|
|
{
|
|
struct kbd_softc *k = (struct kbd_softc *)arg;
|
|
int s = spltty();
|
|
|
|
if (k->k_repeating && k->k_repeatcp != NULL) {
|
|
wscons_input(k->k_repeatcp);
|
|
timeout(kbd_repeat, k, (hz * k->k_repeat_step) / 1000);
|
|
}
|
|
splx(s);
|
|
}
|
|
|
|
void
|
|
kbd_input(register int c)
|
|
{
|
|
register struct kbd_softc *k = &kbd_softc;
|
|
register struct firm_event *fe;
|
|
register int put;
|
|
char *cp;
|
|
|
|
if (k->k_repeating) {
|
|
k->k_repeating = 0;
|
|
untimeout(kbd_repeat, k);
|
|
}
|
|
|
|
/*
|
|
* If /dev/kbd is not connected in event mode translate and
|
|
* send upstream.
|
|
*/
|
|
if (!k->k_evmode) {
|
|
cp = (*k->k_ispec.wi_translate)(k->k_idev, c);
|
|
if (cp != NULL) {
|
|
wscons_input(cp);
|
|
k->k_repeating = 1;
|
|
k->k_repeatcp = cp;
|
|
timeout(kbd_repeat, k, (hz * k->k_repeat_start) / 1000);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Keyboard is generating events. Turn this keystroke into an
|
|
* event and put it in the queue. If the queue is full, the
|
|
* keystroke is lost (sorry!).
|
|
*/
|
|
put = k->k_events.ev_put;
|
|
fe = &k->k_events.ev_q[put];
|
|
put = (put + 1) % EV_QSIZE;
|
|
if (put == k->k_events.ev_get) {
|
|
log(LOG_WARNING, "keyboard event queue overflow\n"); /* ??? */
|
|
return;
|
|
}
|
|
fe->id = c & k->k_ispec.wi_keymask;
|
|
fe->value = (c & k->k_ispec.wi_keyupmask) != 0 ? VKEY_UP : VKEY_DOWN;
|
|
microtime(&fe->time);
|
|
k->k_events.ev_put = put;
|
|
EV_WAKEUP(&k->k_events);
|
|
}
|
|
|
|
int
|
|
kbdopen(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
|
|
if (kbd_softc.k_events.ev_io)
|
|
return (EBUSY);
|
|
kbd_softc.k_events.ev_io = p;
|
|
ev_init(&kbd_softc.k_events);
|
|
kbd_softc.k_evmode = 1;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
kbdclose(dev_t dev, int flags, int mode, struct proc *p)
|
|
{
|
|
|
|
/*
|
|
* Turn off event mode, dump the queue, and close the keyboard
|
|
* unless it is supplying console input.
|
|
*/
|
|
kbd_softc.k_evmode = 0;
|
|
ev_fini(&kbd_softc.k_events);
|
|
kbd_softc.k_events.ev_io = NULL;
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
kbdread(dev_t dev, struct uio *uio, int flags)
|
|
{
|
|
|
|
return (ev_read(&kbd_softc.k_events, uio, flags));
|
|
}
|
|
|
|
/* this routine should not exist, but is convenient to write here for now */
|
|
int
|
|
kbdwrite(dev_t dev, struct uio *uio, int flags)
|
|
{
|
|
|
|
return (EOPNOTSUPP);
|
|
}
|
|
|
|
int
|
|
kbdioctl(dev_t dev, u_long cmd, register caddr_t data, int flag, struct proc *p)
|
|
{
|
|
struct kbd_softc *k = &kbd_softc;
|
|
struct wsconsio_bell_data *wbd;
|
|
int rv;
|
|
|
|
rv = ENOTTY;
|
|
switch (cmd) {
|
|
#if 0
|
|
case KIOCSDIRECT:
|
|
k->k_evmode = *(int *)data;
|
|
return (0);
|
|
#endif
|
|
|
|
case FIONBIO: /* we will remove this someday (soon???) */
|
|
return (0);
|
|
|
|
case FIOASYNC:
|
|
k->k_events.ev_async = *(int *)data != 0;
|
|
return (0);
|
|
|
|
case TIOCSPGRP:
|
|
if (*(int *)data != k->k_events.ev_io->p_pgid)
|
|
return (EPERM);
|
|
return (0);
|
|
|
|
case WSCONSIO_BELL:
|
|
if (k->k_ispec.wi_bell != NULL)
|
|
(*k->k_ispec.wi_bell)(k->k_idev, &k->k_belldata);
|
|
return (0);
|
|
|
|
case WSCONSIO_COMPLEXBELL:
|
|
if (k->k_ispec.wi_bell != NULL) {
|
|
wbd = (struct wsconsio_bell_data *)data;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_PITCH) == 0)
|
|
wbd->wbd_pitch = k->k_belldata.wbd_pitch;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_PERIOD) == 0)
|
|
wbd->wbd_period = k->k_belldata.wbd_period;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_VOLUME) == 0)
|
|
wbd->wbd_volume = k->k_belldata.wbd_volume;
|
|
|
|
(*k->k_ispec.wi_bell)(k->k_idev, wbd);
|
|
}
|
|
return (0);
|
|
|
|
case WSCONSIO_SETBELL:
|
|
wbd = (struct wsconsio_bell_data *)data;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_PITCH) != 0)
|
|
k->k_belldata.wbd_pitch = wbd->wbd_pitch;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_PERIOD) != 0)
|
|
k->k_belldata.wbd_period = wbd->wbd_period;
|
|
if ((wbd->wbd_flags & WSCONSIO_BELLDATA_VOLUME) != 0)
|
|
k->k_belldata.wbd_volume = wbd->wbd_volume;
|
|
return (0);
|
|
|
|
case WSCONSIO_GETBELL:
|
|
wbd = (struct wsconsio_bell_data *)data;
|
|
wbd->wbd_flags = WSCONSIO_BELLDATA_PITCH |
|
|
WSCONSIO_BELLDATA_PERIOD | WSCONSIO_BELLDATA_VOLUME;
|
|
wbd->wbd_pitch = k->k_belldata.wbd_pitch;
|
|
wbd->wbd_period = k->k_belldata.wbd_period;
|
|
wbd->wbd_volume = k->k_belldata.wbd_volume ;
|
|
return (0);
|
|
|
|
#if 0 /* XXX */
|
|
/* XXX KEY-REPEAT RATE SETTING */
|
|
#endif /* XXX */
|
|
|
|
default:
|
|
if (k->k_ispec.wi_ioctl != NULL)
|
|
rv = (*k->k_ispec.wi_ioctl)(k->k_idev, cmd, data,
|
|
flag, p);
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
kbdpoll(dev_t dev, int events, struct proc *p)
|
|
{
|
|
|
|
return (ev_poll(&kbd_softc.k_events, events, p));
|
|
}
|
|
|
|
/* Ring the console bell. (For wscons terminal emulator and other code) */
|
|
void
|
|
wscons_kbd_bell()
|
|
{
|
|
struct kbd_softc *k = &kbd_softc;
|
|
|
|
if (k->k_ispec.wi_bell != NULL)
|
|
(*k->k_ispec.wi_bell)(k->k_idev, &k->k_belldata);
|
|
}
|
|
|
|
/*
|
|
* Console handling functions.
|
|
*/
|
|
|
|
int
|
|
kbd_cngetc(dev)
|
|
dev_t dev;
|
|
{
|
|
struct kbd_softc *k = &kbd_softc;
|
|
|
|
if (kbd_softc.k_evmode) /* XXX? */
|
|
return 0;
|
|
if (k->k_ispec.wi_getc != NULL)
|
|
return (*k->k_ispec.wi_getc)(k->k_idev);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
kbd_cnpollc(dev, on)
|
|
dev_t dev;
|
|
int on;
|
|
{
|
|
struct kbd_softc *k = &kbd_softc;
|
|
|
|
if (kbd_softc.k_evmode) /* XXX? */
|
|
return;
|
|
if (k->k_ispec.wi_pollc != NULL)
|
|
(*k->k_ispec.wi_pollc)(k->k_idev, on);
|
|
}
|