2000-05-13 16:17:49 +04:00
|
|
|
|
/* $NetBSD: arckbd.c,v 1.2 2000/05/13 12:17:49 bjh21 Exp $ */
|
2000-05-10 01:55:44 +04:00
|
|
|
|
/*-
|
|
|
|
|
* Copyright (c) 1998, 1999, 2000 Ben Harris
|
|
|
|
|
* 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 ``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 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.
|
|
|
|
|
*/
|
|
|
|
|
/* This file is part of NetBSD/arm26 -- a port of NetBSD to ARM2/3 machines. */
|
|
|
|
|
/*
|
|
|
|
|
* arckbd.c - Archimedes keyboard driver
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Most of the information used to write this driver came from the
|
|
|
|
|
* A3000 Technical Reference Manual (ISBN 1-85250-074-3).
|
|
|
|
|
*
|
|
|
|
|
* We keep a queue of one command in the softc for use by the recieve
|
|
|
|
|
* interrupt handler in case it finds the KART is already transmitting
|
|
|
|
|
* a command (presumably as a consequence of a user request) when it
|
|
|
|
|
* wants to. I think this is safe in all cases, and it will never
|
|
|
|
|
* happen more than once at a time (I hope).
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
|
|
2000-05-13 16:17:49 +04:00
|
|
|
|
__RCSID("$NetBSD: arckbd.c,v 1.2 2000/05/13 12:17:49 bjh21 Exp $");
|
2000-05-10 01:55:44 +04:00
|
|
|
|
|
|
|
|
|
#include <sys/device.h>
|
|
|
|
|
#include <sys/errno.h>
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
|
#include <sys/proc.h>
|
|
|
|
|
#include <sys/syslog.h>
|
|
|
|
|
#include <sys/systm.h>
|
|
|
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
|
#include <machine/irq.h>
|
|
|
|
|
#include <machine/spl.h>
|
|
|
|
|
|
|
|
|
|
#include <dev/wscons/wsconsio.h>
|
|
|
|
|
#include <dev/wscons/wskbdvar.h>
|
|
|
|
|
#include <dev/wscons/wsksymdef.h>
|
|
|
|
|
#include <dev/wscons/wsmousevar.h>
|
|
|
|
|
|
|
|
|
|
#include <arch/arm26/iobus/iocreg.h>
|
|
|
|
|
#include <arch/arm26/iobus/iocvar.h>
|
|
|
|
|
#include <arch/arm26/ioc/arckbdreg.h>
|
|
|
|
|
#include <arch/arm26/ioc/arckbdvar.h>
|
|
|
|
|
|
|
|
|
|
#include "locators.h"
|
|
|
|
|
|
|
|
|
|
/* #define ARCKBD_DEBUG */
|
|
|
|
|
|
|
|
|
|
enum arckbd_state {
|
|
|
|
|
AS_HRST, AS_RAK1, AS_RAK2, /* reset protocol */
|
|
|
|
|
AS_IDLE, /* idle, waiting for data */
|
|
|
|
|
AS_KDDA, AS_KUDA, AS_MDAT /* Receiving two-byte message */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char *arckbd_statenames[] = {
|
|
|
|
|
"hrst", "rak1", "rak2", "idle", "kdda", "kuda", "mdat"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int arckbd_match __P((struct device *parent, struct cfdata *cf, void *aux));
|
|
|
|
|
static void arckbd_attach __P((struct device *parent, struct device *self, void *aux));
|
|
|
|
|
static kbd_t arckbd_pick_layout __P((int kbid));
|
|
|
|
|
static int arcwskbd_match __P((struct device *parent, struct cfdata *cf, void *aux));
|
|
|
|
|
static void arcwskbd_attach __P((struct device *parent, struct device *self, void *aux));
|
|
|
|
|
static int arcwsmouse_match __P((struct device *parent, struct cfdata *cf, void *aux));
|
|
|
|
|
static void arcwsmouse_attach __P((struct device *parent, struct device *self, void *aux));
|
|
|
|
|
|
|
|
|
|
static int arckbd_rint __P((void *self));
|
|
|
|
|
static int arckbd_xint __P((void *self));
|
|
|
|
|
static void arckbd_mousemoved __P((struct device *self, int byte1, int byte2));
|
|
|
|
|
static void arckbd_keyupdown __P((struct device *self, int byte1, int byte2));
|
|
|
|
|
static int arckbd_send __P((struct device *self, int data, enum arckbd_state newstate, int waitok));
|
|
|
|
|
|
|
|
|
|
static int arckbd_enable __P((void *cookie, int on));
|
|
|
|
|
static int arckbd_led_encode __P((int));
|
|
|
|
|
static int arckbd_led_decode __P((int));
|
|
|
|
|
static void arckbd_set_leds __P((void *cookie, int new_state));
|
|
|
|
|
static int arckbd_ioctl __P((void *cookie, u_long cmd, caddr_t data, int flag, struct proc *p));
|
|
|
|
|
static void arckbd_getc __P((void *cookie, u_int *typep, int *valuep));
|
|
|
|
|
static void arckbd_pollc __P((void *cookie, int poll));
|
|
|
|
|
static int arcmouse_enable __P((void *cookie));
|
|
|
|
|
static int arcmouse_ioctl __P((void *cookie, u_long cmd, caddr_t data, int flag, struct proc *p));
|
|
|
|
|
static void arcmouse_disable __P((void *cookie));
|
|
|
|
|
|
|
|
|
|
struct arckbd_softc {
|
|
|
|
|
struct device sc_dev;
|
|
|
|
|
bus_space_tag_t sc_bst;
|
|
|
|
|
bus_space_handle_t sc_bsh;
|
|
|
|
|
u_int sc_mouse_buttons;
|
|
|
|
|
enum arckbd_state sc_state;
|
|
|
|
|
u_char sc_byteone;
|
|
|
|
|
u_char sc_kbid;
|
|
|
|
|
struct device *sc_wskbddev;
|
|
|
|
|
struct device *sc_wsmousedev;
|
|
|
|
|
struct wskbd_mapdata sc_mapdata;
|
|
|
|
|
int sc_cmdqueue; /* Single-command queue */
|
|
|
|
|
enum arckbd_state sc_statequeue;
|
|
|
|
|
int sc_cmdqueued;
|
|
|
|
|
int sc_flags;
|
|
|
|
|
int sc_leds;
|
|
|
|
|
u_int sc_poll_type;
|
|
|
|
|
int sc_poll_value;
|
|
|
|
|
struct irq_handler *sc_xirq;
|
|
|
|
|
struct irq_handler *sc_rirq;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define AKF_WANTKBD 0x01
|
|
|
|
|
#define AKF_WANTMOUSE 0x02
|
|
|
|
|
#define AKF_SENTRQID 0x04
|
|
|
|
|
#define AKF_SENTLEDS 0x08
|
|
|
|
|
#define AKF_POLLING 0x10
|
|
|
|
|
|
|
|
|
|
struct cfattach arckbd_ca = {
|
|
|
|
|
sizeof(struct arckbd_softc), arckbd_match, arckbd_attach
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Internal devices used because arckbd can't be both a wskbddev and a
|
|
|
|
|
* wsmousedev. I suspect the right way to do this is through an
|
|
|
|
|
* "attach wskbd at arckbd with wskbd_arckbd" type thing, but we can't
|
|
|
|
|
* as the size of a wskbd_softc isn't public. Come to think of it,
|
|
|
|
|
* this isn't very evil -- I just don't like having yet another line
|
|
|
|
|
* of configuration.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct cfattach arcwskbd_ca = {
|
|
|
|
|
sizeof(struct device), arcwskbd_match, arcwskbd_attach
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct cfattach arcwsmouse_ca = {
|
|
|
|
|
sizeof(struct device), arcwsmouse_match, arcwsmouse_attach
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct arckbd_attach_args {
|
|
|
|
|
enum { ARCKBD_KBDDEV, ARCKBD_MOUSEDEV } aka_devtype;
|
|
|
|
|
struct wskbddev_attach_args aka_wskbdargs;
|
|
|
|
|
struct wsmousedev_attach_args aka_wsmouseargs;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extern struct cfdriver arckbd_cd, arcwskbd_cd, arcwsmouse_cd;
|
|
|
|
|
|
|
|
|
|
static struct wskbd_accessops arckbd_accessops = {
|
|
|
|
|
arckbd_enable, arckbd_set_leds, arckbd_ioctl
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct wskbd_consops arckbd_consops = {
|
|
|
|
|
arckbd_getc, arckbd_pollc
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static struct wsmouse_accessops arcmouse_accessops = {
|
|
|
|
|
arcmouse_enable, arcmouse_ioctl, arcmouse_disable
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
static int
|
|
|
|
|
arckbd_match(parent, cf, aux)
|
|
|
|
|
struct device *parent;
|
|
|
|
|
struct cfdata *cf;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* Assume presence for now */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arckbd_attach(parent, self, aux)
|
|
|
|
|
struct device *parent, *self;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = (void *)self;
|
|
|
|
|
struct ioc_attach_args *ioc = aux;
|
|
|
|
|
bus_space_tag_t bst;
|
|
|
|
|
bus_space_handle_t bsh;
|
|
|
|
|
struct arckbd_attach_args aka;
|
|
|
|
|
|
|
|
|
|
bst = sc->sc_bst = ioc->ioc_fast_t;
|
|
|
|
|
bsh = sc->sc_bsh = ioc->ioc_fast_h;
|
|
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
sc->sc_rirq = ioc_irq_establish(sc->sc_dev.dv_parent, IOC_IRQ_SRX,
|
|
|
|
|
IPL_TTY, arckbd_rint, self);
|
|
|
|
|
printf("%s: interrupting at %s (rx)", self->dv_xname,
|
|
|
|
|
irq_string(sc->sc_rirq));
|
|
|
|
|
sc->sc_xirq = ioc_irq_establish(sc->sc_dev.dv_parent, IOC_IRQ_STX,
|
|
|
|
|
IPL_TTY, arckbd_xint, self);
|
|
|
|
|
printf(" and %s (tx)", irq_string(sc->sc_xirq));
|
|
|
|
|
irq_enable(sc->sc_rirq);
|
|
|
|
|
|
|
|
|
|
/* Initialisation of IOC KART per IOC Data Sheet section 6.2.3. */
|
|
|
|
|
|
|
|
|
|
/* Set up IOC counter 3 */
|
|
|
|
|
/* k_BAUD = 1/((latch+1)*16) MHz */
|
|
|
|
|
ioc_counter_start(parent, 3, 62500 / ARCKBD_BAUD - 1);
|
|
|
|
|
|
|
|
|
|
/* Read from Rx register and discard. */
|
|
|
|
|
(void)bus_space_read_1(bst, bsh, 0);
|
|
|
|
|
|
|
|
|
|
/* Kick the keyboard into life */
|
|
|
|
|
arckbd_send(self, ARCKBD_HRST, AS_HRST, 0);
|
|
|
|
|
|
|
|
|
|
sc->sc_mapdata = arckbd_mapdata_default;
|
|
|
|
|
sc->sc_mapdata.layout = KB_UK; /* Reasonable default */
|
|
|
|
|
|
|
|
|
|
/* XXX set the LEDs to a known state? (or will wskbd do this?) */
|
|
|
|
|
|
|
|
|
|
/* Attach the wskbd console */
|
|
|
|
|
arckbd_cnattach(self);
|
|
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
/* Attach the dummy drivers */
|
|
|
|
|
aka.aka_wskbdargs.console = 1; /* XXX FIXME */
|
|
|
|
|
aka.aka_wskbdargs.keymap = &sc->sc_mapdata;
|
|
|
|
|
aka.aka_wskbdargs.accessops = &arckbd_accessops;
|
|
|
|
|
aka.aka_wskbdargs.accesscookie = self;
|
|
|
|
|
aka.aka_wsmouseargs.accessops = &arcmouse_accessops;
|
|
|
|
|
aka.aka_wsmouseargs.accesscookie = self;
|
|
|
|
|
aka.aka_devtype = ARCKBD_KBDDEV;
|
|
|
|
|
config_found(self, &aka, NULL);
|
|
|
|
|
aka.aka_devtype = ARCKBD_MOUSEDEV;
|
|
|
|
|
config_found(self, &aka, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static kbd_t
|
|
|
|
|
arckbd_pick_layout(kbid)
|
|
|
|
|
int kbid;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; arckbd_kbidtab[i].kbid != 0; i++) {
|
|
|
|
|
if (arckbd_kbidtab[i].kbid == kbid)
|
|
|
|
|
return arckbd_kbidtab[i].layout;
|
|
|
|
|
}
|
|
|
|
|
return KB_UK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
static int
|
|
|
|
|
arcwskbd_match(parent, cf, aux)
|
|
|
|
|
struct device *parent;
|
|
|
|
|
struct cfdata *cf;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_attach_args *aka = aux;
|
|
|
|
|
|
|
|
|
|
if (aka->aka_devtype == ARCKBD_KBDDEV)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
static int
|
|
|
|
|
arcwsmouse_match(parent, cf, aux)
|
|
|
|
|
struct device *parent;
|
|
|
|
|
struct cfdata *cf;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_attach_args *aka = aux;
|
|
|
|
|
|
|
|
|
|
if (aka->aka_devtype == ARCKBD_MOUSEDEV)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arcwskbd_attach(parent, self, aux)
|
|
|
|
|
struct device *parent, *self;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_attach_args *aka = aux;
|
|
|
|
|
struct arckbd_softc *sc = (void *)parent;
|
|
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
sc->sc_wskbddev = config_found(self, &(aka->aka_wskbdargs),
|
|
|
|
|
wskbddevprint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arcwsmouse_attach(parent, self, aux)
|
|
|
|
|
struct device *parent, *self;
|
|
|
|
|
void *aux;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_attach_args *aka = aux;
|
|
|
|
|
struct arckbd_softc *sc = (void *)parent;
|
|
|
|
|
|
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
|
|
sc->sc_wsmousedev = config_found(self, &(aka->aka_wsmouseargs),
|
|
|
|
|
wsmousedevprint);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We don't really _need_ a console keyboard before
|
|
|
|
|
* autoconfiguration's finished, so for now this function's written to
|
|
|
|
|
* be called from arckbd_attach. The console functions should still
|
|
|
|
|
* try not to rely on much, and perhaps one day we should make it
|
|
|
|
|
* happen in consinit instead.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
arckbd_cnattach(self)
|
|
|
|
|
struct device *self;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = (void*)self;
|
|
|
|
|
|
|
|
|
|
wskbd_cnattach(&arckbd_consops, sc, &arckbd_mapdata_default);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arckbd_getc(cookie, typep, valuep)
|
|
|
|
|
void *cookie;
|
|
|
|
|
u_int *typep;
|
|
|
|
|
int *valuep;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
int s;
|
|
|
|
|
|
|
|
|
|
if (!(sc->sc_flags & AKF_POLLING))
|
|
|
|
|
panic("%s: arckbd_getc called with polling disabled",
|
|
|
|
|
sc->sc_dev.dv_xname);
|
|
|
|
|
while (sc->sc_poll_type == 0) {
|
|
|
|
|
if (ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_STX))
|
|
|
|
|
arckbd_xint(&sc->sc_dev);
|
|
|
|
|
if (ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_SRX))
|
|
|
|
|
arckbd_rint(&sc->sc_dev);
|
|
|
|
|
}
|
|
|
|
|
s = spltty();
|
|
|
|
|
*typep = sc->sc_poll_type;
|
|
|
|
|
*valuep = sc->sc_poll_value;
|
|
|
|
|
sc->sc_poll_type = 0;
|
|
|
|
|
sc->sc_poll_value = 0;
|
|
|
|
|
splx(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arckbd_pollc(cookie, poll)
|
|
|
|
|
void *cookie;
|
|
|
|
|
int poll;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
int s;
|
|
|
|
|
|
|
|
|
|
s = spltty();
|
|
|
|
|
if (poll) {
|
|
|
|
|
sc->sc_flags |= AKF_POLLING;
|
|
|
|
|
irq_disable(sc->sc_rirq);
|
|
|
|
|
irq_disable(sc->sc_xirq);
|
|
|
|
|
} else {
|
|
|
|
|
sc->sc_flags &= ~AKF_POLLING;
|
|
|
|
|
irq_enable(sc->sc_rirq);
|
|
|
|
|
irq_enable(sc->sc_xirq);
|
|
|
|
|
}
|
|
|
|
|
splx(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_send(self, data, newstate, waitok)
|
|
|
|
|
struct device *self;
|
|
|
|
|
int data, waitok;
|
|
|
|
|
enum arckbd_state newstate;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = (void *)self;
|
|
|
|
|
int s, res;
|
|
|
|
|
bus_space_tag_t bst = sc->sc_bst;
|
|
|
|
|
bus_space_handle_t bsh = sc->sc_bsh;
|
|
|
|
|
|
|
|
|
|
s = spltty();
|
|
|
|
|
if (waitok) {
|
|
|
|
|
while (!ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_STX))
|
|
|
|
|
if ((sc->sc_flags & AKF_POLLING) == 0) {
|
|
|
|
|
res = tsleep(arckbd_send, PWAIT, "kbdsend", 0);
|
|
|
|
|
if (res != 0)
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
} else if (!ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_STX)) {
|
|
|
|
|
if (sc->sc_cmdqueued)
|
|
|
|
|
panic("%s: queue overflow", sc->sc_dev.dv_xname);
|
|
|
|
|
else {
|
|
|
|
|
sc->sc_cmdqueue = data;
|
|
|
|
|
sc->sc_statequeue = newstate;
|
|
|
|
|
sc->sc_cmdqueued = 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bus_space_barrier(bst, bsh, 0, 1, BUS_BARRIER_WRITE);
|
|
|
|
|
bus_space_write_1(bst, bsh, 0, data);
|
|
|
|
|
bus_space_barrier(bst, bsh, 0, 1, BUS_BARRIER_WRITE);
|
|
|
|
|
sc->sc_state = newstate;
|
|
|
|
|
#ifdef ARCKBD_DEBUG
|
|
|
|
|
log(LOG_DEBUG, "%s: sent 0x%02x. now in state %s\n",
|
|
|
|
|
sc->sc_dev.dv_xname, data, arckbd_statenames[newstate]);
|
|
|
|
|
#endif
|
|
|
|
|
wakeup(&sc->sc_state);
|
|
|
|
|
splx(s);
|
|
|
|
|
if ((sc->sc_flags & AKF_POLLING) == 0)
|
|
|
|
|
irq_enable(sc->sc_xirq);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_xint(cookie)
|
|
|
|
|
void *cookie;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
|
|
|
|
|
if (!ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_STX)) {
|
|
|
|
|
printf("%s: Stray tx interrupt\n", sc->sc_dev.dv_xname);
|
|
|
|
|
return IRQ_NOT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
irq_disable(sc->sc_xirq);
|
|
|
|
|
/* First, process queued commands (acks from the last receive) */
|
|
|
|
|
if (sc->sc_cmdqueued) {
|
|
|
|
|
sc->sc_cmdqueued = 0;
|
|
|
|
|
arckbd_send(&sc->sc_dev, sc->sc_cmdqueue, sc->sc_statequeue, 0);
|
|
|
|
|
} else if (sc->sc_state == AS_IDLE) {
|
|
|
|
|
/* Do things that need doing after a reset */
|
|
|
|
|
if (!(sc->sc_flags & AKF_SENTRQID)) {
|
|
|
|
|
arckbd_send(&sc->sc_dev, ARCKBD_RQID, AS_IDLE, 0);
|
|
|
|
|
sc->sc_flags |= AKF_SENTRQID;
|
|
|
|
|
} else if (!(sc->sc_flags & AKF_SENTLEDS)) {
|
|
|
|
|
arckbd_send(&sc->sc_dev, ARCKBD_LEDS | sc->sc_leds,
|
|
|
|
|
AS_IDLE, 0);
|
|
|
|
|
sc->sc_flags |= AKF_SENTLEDS;
|
|
|
|
|
}
|
|
|
|
|
} else if ((sc->sc_flags & AKF_POLLING) == 0)
|
|
|
|
|
wakeup(&arckbd_send);
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_rint(cookie)
|
|
|
|
|
void *cookie;
|
|
|
|
|
{
|
|
|
|
|
struct device *self = cookie;
|
|
|
|
|
struct arckbd_softc *sc = (void *)self;
|
|
|
|
|
bus_space_tag_t bst = sc->sc_bst;
|
|
|
|
|
bus_space_handle_t bsh = sc->sc_bsh;
|
|
|
|
|
int data;
|
|
|
|
|
|
|
|
|
|
if (!ioc_irq_status(sc->sc_dev.dv_parent, IOC_IRQ_SRX)) {
|
|
|
|
|
printf("%s: Stray rx interrupt\n", sc->sc_dev.dv_xname);
|
|
|
|
|
return IRQ_NOT_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
bus_space_barrier(bst, bsh, 0, 1, BUS_BARRIER_READ);
|
|
|
|
|
data = bus_space_read_1(bst, bsh, 0);
|
|
|
|
|
bus_space_barrier(bst, bsh, 0, 1, BUS_BARRIER_READ);
|
|
|
|
|
/* Reset protocol */
|
|
|
|
|
#ifdef ARCKBD_DEBUG
|
|
|
|
|
log(LOG_DEBUG, "%s: got 0x%02x in state %s\n", self->dv_xname, data,
|
|
|
|
|
arckbd_statenames[sc->sc_state]);
|
|
|
|
|
#endif
|
|
|
|
|
if (data == ARCKBD_HRST && sc->sc_state == AS_HRST)
|
|
|
|
|
arckbd_send(self, ARCKBD_RAK1, AS_RAK1, 0);
|
|
|
|
|
else if (data == ARCKBD_RAK1 &&
|
|
|
|
|
(sc->sc_state == AS_RAK1 || sc->sc_state == AS_HRST))
|
|
|
|
|
arckbd_send(self, ARCKBD_RAK2, AS_RAK2, 0);
|
|
|
|
|
else if (data == ARCKBD_RAK2 && sc->sc_state == AS_RAK2)
|
|
|
|
|
arckbd_send(self, ARCKBD_SMAK, AS_IDLE, 0);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note that for data messages, we acknowledge first and
|
|
|
|
|
* _then_ process the data. This is important because the
|
|
|
|
|
* processing may end up trying to use the keyboard in polled
|
|
|
|
|
* mode (e.g. through DDB) and we'd like its state to be
|
|
|
|
|
* self-consistent.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Mouse data */
|
|
|
|
|
else if (ARCKBD_IS_MDAT(data) && sc->sc_state == AS_IDLE) {
|
|
|
|
|
arckbd_send(self, ARCKBD_BACK, AS_MDAT, 0);
|
|
|
|
|
sc->sc_byteone = data;
|
|
|
|
|
} else if (ARCKBD_IS_MDAT(data) && sc->sc_state == AS_MDAT) {
|
|
|
|
|
arckbd_send(self, ARCKBD_SMAK, AS_IDLE, 0);
|
|
|
|
|
arckbd_mousemoved(self, sc->sc_byteone, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Key down data */
|
|
|
|
|
else if (ARCKBD_IS_KDDA(data) && sc->sc_state == AS_IDLE) {
|
|
|
|
|
arckbd_send(self, ARCKBD_BACK, AS_KDDA, 0);
|
|
|
|
|
sc->sc_byteone = data;
|
|
|
|
|
} else if (ARCKBD_IS_KDDA(data) && sc->sc_state == AS_KDDA) {
|
|
|
|
|
arckbd_send(self, ARCKBD_SMAK, AS_IDLE, 0);
|
|
|
|
|
arckbd_keyupdown(self, sc->sc_byteone, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Key up data */
|
|
|
|
|
else if (ARCKBD_IS_KUDA(data) && sc->sc_state == AS_IDLE) {
|
|
|
|
|
arckbd_send(self, ARCKBD_BACK, AS_KUDA, 0);
|
|
|
|
|
sc->sc_byteone = data;
|
|
|
|
|
} else if (ARCKBD_IS_KUDA(data) && sc->sc_state == AS_KUDA) {
|
|
|
|
|
arckbd_send(self, ARCKBD_SMAK, AS_IDLE, 0);
|
|
|
|
|
arckbd_keyupdown(self, sc->sc_byteone, data);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Other cruft */
|
|
|
|
|
else if (ARCKBD_IS_KBID(data)) {
|
|
|
|
|
arckbd_send(self, ARCKBD_SMAK, AS_IDLE, 0);
|
|
|
|
|
if (sc->sc_kbid != data) {
|
|
|
|
|
printf("%s: layout %d\n",
|
|
|
|
|
self->dv_xname, data & ~ARCKBD_KBID);
|
|
|
|
|
sc->sc_kbid = data;
|
|
|
|
|
}
|
|
|
|
|
} else if (ARCKBD_IS_PDAT(data))
|
|
|
|
|
/* unused -- ignore it */;
|
|
|
|
|
else {
|
|
|
|
|
/* Protocol error */
|
|
|
|
|
log(LOG_WARNING, "%s: protocol error: got 0x%02x in state %s\n",
|
|
|
|
|
self->dv_xname, data, arckbd_statenames[sc->sc_state]);
|
|
|
|
|
arckbd_send(self, ARCKBD_HRST, AS_HRST, 0);
|
|
|
|
|
}
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arckbd_mousemoved(self, byte1, byte2)
|
|
|
|
|
struct device *self;
|
|
|
|
|
int byte1, byte2;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = (void *)self;
|
|
|
|
|
int dx, dy;
|
|
|
|
|
|
|
|
|
|
if (sc->sc_wsmousedev != NULL) {
|
|
|
|
|
/* deltas are 7-bit signed */
|
|
|
|
|
dx = byte1 < 0x40 ? byte1 : byte1 - 0x80;
|
|
|
|
|
dy = byte2 < 0x40 ? byte2 : byte2 - 0x80;
|
|
|
|
|
wsmouse_input(sc->sc_wsmousedev,
|
|
|
|
|
sc->sc_mouse_buttons, dx, dy, 0,
|
|
|
|
|
WSMOUSE_INPUT_DELTA);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arckbd_keyupdown(self, byte1, byte2)
|
|
|
|
|
struct device *self;
|
|
|
|
|
int byte1, byte2;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = (void *)self;
|
|
|
|
|
u_int type;
|
|
|
|
|
int value;
|
|
|
|
|
|
|
|
|
|
if ((byte1 & 0x0f) == 7) {
|
|
|
|
|
/* Mouse button event */
|
|
|
|
|
/*
|
|
|
|
|
* This is all very silly, as the wsmouse driver then
|
|
|
|
|
* differentiates the button state to see if there's
|
|
|
|
|
* an event worth passing to the user.
|
|
|
|
|
*
|
|
|
|
|
* Oh well, at least NetBSD and Acorn number their
|
|
|
|
|
* mouse buttons the same way.
|
|
|
|
|
*/
|
|
|
|
|
if (ARCKBD_IS_KDDA(byte1))
|
|
|
|
|
sc->sc_mouse_buttons |= (1 << (byte2 & 0x0f));
|
|
|
|
|
else
|
|
|
|
|
sc->sc_mouse_buttons &= ~(1 << (byte2 & 0x0f));
|
|
|
|
|
if (sc->sc_wsmousedev != NULL)
|
|
|
|
|
wsmouse_input(sc->sc_wsmousedev, sc->sc_mouse_buttons,
|
|
|
|
|
0, 0, 0, WSMOUSE_INPUT_DELTA);
|
|
|
|
|
} else {
|
|
|
|
|
type = ARCKBD_IS_KDDA(byte1) ?
|
|
|
|
|
WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP;
|
|
|
|
|
value = ((byte1 & 0x0f) << 4) | (byte2 & 0x0f);
|
|
|
|
|
if (sc->sc_flags & AKF_POLLING) {
|
|
|
|
|
sc->sc_poll_type = type;
|
|
|
|
|
sc->sc_poll_value = value;
|
|
|
|
|
} else if (sc->sc_wskbddev != NULL)
|
|
|
|
|
wskbd_input(sc->sc_wskbddev, type, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Keyboard access functions
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_enable(cookie, on)
|
|
|
|
|
void *cookie;
|
|
|
|
|
int on;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
|
|
|
|
|
if (on) {
|
|
|
|
|
sc->sc_flags |= AKF_WANTKBD;
|
|
|
|
|
/* XXX send RQMP? */
|
|
|
|
|
} else
|
|
|
|
|
sc->sc_flags &= ~ AKF_WANTKBD;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_led_encode(wsleds)
|
|
|
|
|
int wsleds;
|
|
|
|
|
{
|
|
|
|
|
int arcleds;
|
|
|
|
|
|
|
|
|
|
arcleds = 0;
|
|
|
|
|
if (wsleds & WSKBD_LED_CAPS)
|
|
|
|
|
arcleds |= ARCKBD_LEDS_CAPSLOCK;
|
|
|
|
|
if (wsleds & WSKBD_LED_NUM)
|
|
|
|
|
arcleds |= ARCKBD_LEDS_NUMLOCK;
|
|
|
|
|
if (wsleds & WSKBD_LED_SCROLL)
|
|
|
|
|
arcleds |= ARCKBD_LEDS_SCROLLLOCK;
|
|
|
|
|
/* No "compose" LED */
|
|
|
|
|
return arcleds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arckbd_led_decode(arcleds)
|
|
|
|
|
int arcleds;
|
|
|
|
|
{
|
|
|
|
|
int wsleds;
|
|
|
|
|
|
|
|
|
|
wsleds = 0;
|
|
|
|
|
if (arcleds & ARCKBD_LEDS_CAPSLOCK)
|
|
|
|
|
wsleds |= WSKBD_LED_CAPS;
|
|
|
|
|
if (arcleds & ARCKBD_LEDS_NUMLOCK)
|
|
|
|
|
wsleds |= WSKBD_LED_NUM;
|
|
|
|
|
if (arcleds & ARCKBD_LEDS_SCROLLLOCK)
|
|
|
|
|
wsleds |= WSKBD_LED_SCROLL;
|
|
|
|
|
/* No "compose" LED */
|
|
|
|
|
return wsleds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Set the LEDs to the requested state.
|
|
|
|
|
*
|
|
|
|
|
* Be warned: This function gets called from interrupts.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
arckbd_set_leds(cookie, new_state)
|
|
|
|
|
void *cookie;
|
|
|
|
|
int new_state;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
int s;
|
|
|
|
|
|
|
|
|
|
s = spltty();
|
|
|
|
|
sc->sc_leds = arckbd_led_encode(new_state);
|
|
|
|
|
if (arckbd_send(cookie, ARCKBD_LEDS | sc->sc_leds, AS_IDLE, 0) == 0)
|
|
|
|
|
sc->sc_flags |= AKF_SENTLEDS;
|
|
|
|
|
splx(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
static int
|
|
|
|
|
arckbd_ioctl(cookie, cmd, data, flag, p)
|
|
|
|
|
void *cookie;
|
|
|
|
|
u_long cmd;
|
|
|
|
|
caddr_t data;
|
|
|
|
|
int flag;
|
|
|
|
|
struct proc *p;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case WSKBDIO_GTYPE:
|
|
|
|
|
*(int *)data = WSKBD_TYPE_ARCHIMEDES;
|
|
|
|
|
return 0;
|
|
|
|
|
case WSKBDIO_SETLEDS:
|
|
|
|
|
arckbd_set_leds(sc, *(int *)data);
|
|
|
|
|
return 0;
|
|
|
|
|
case WSKBDIO_GETLEDS:
|
|
|
|
|
*(int *)data = arckbd_led_decode(sc->sc_leds);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Mouse access functions
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
arcmouse_enable(cookie)
|
|
|
|
|
void *cookie;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
|
|
|
|
|
sc->sc_flags |= AKF_WANTMOUSE;
|
|
|
|
|
/* XXX send RQMP */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ARGSUSED */
|
|
|
|
|
static int
|
|
|
|
|
arcmouse_ioctl(cookie, cmd, data, flag, p)
|
|
|
|
|
void *cookie;
|
|
|
|
|
u_long cmd;
|
|
|
|
|
caddr_t data;
|
|
|
|
|
int flag;
|
|
|
|
|
struct proc *p;
|
|
|
|
|
{
|
|
|
|
|
/* struct arckbd_softc *sc = cookie; */
|
|
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
|
case WSMOUSEIO_GTYPE:
|
|
|
|
|
*(int *)data = WSMOUSE_TYPE_ARCHIMEDES;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
arcmouse_disable(cookie)
|
|
|
|
|
void *cookie;
|
|
|
|
|
{
|
|
|
|
|
struct arckbd_softc *sc = cookie;
|
|
|
|
|
|
|
|
|
|
sc->sc_flags &= ~AKF_WANTMOUSE;
|
|
|
|
|
}
|