dff5222d3a
drivers that attach to it. This allows for other host interface chips that use the same keyboards and mice, such as the ones in the ARM IOMD20, ARM7500, and SA-1111. The PC-compatible driver is still called pckbc(4), and the new abstraction layer is "pckbport", so the child devices have moved from sys/dev/pckbc to sys/dev/pckbport, which also contains some code shared between all host controllers. To avoid incompatibility, pckbdreg.h is still installed in /usr/include/dev/pckbc. In theory, this shouldn't cause any behavioural changes in the drivers concerned. Thy just use rather more function pointers than before. Tested on i386 and (with a new host driver) acorn32. Compiled on several other affected architectures.
482 lines
11 KiB
C
482 lines
11 KiB
C
/* $NetBSD: hpckbd.c,v 1.11 2004/03/13 17:31:34 bjh21 Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1999-2001 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by UCHIYAMA Yasushi.
|
|
*
|
|
* 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 NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: hpckbd.c,v 1.11 2004/03/13 17:31:34 bjh21 Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/device.h>
|
|
|
|
#include <sys/tty.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/intr.h>
|
|
|
|
#include <machine/config_hook.h>
|
|
#include <machine/platid.h>
|
|
#include <machine/platid_mask.h>
|
|
|
|
#include "opt_wsdisplay_compat.h"
|
|
#include "opt_pckbd_layout.h"
|
|
#include <dev/wscons/wsksymdef.h>
|
|
#include <dev/wscons/wsconsio.h>
|
|
#include <dev/wscons/wskbdvar.h>
|
|
#include <dev/wscons/wsksymdef.h>
|
|
#include <dev/wscons/wsksymvar.h>
|
|
#include <dev/pckbport/wskbdmap_mfii.h>
|
|
#ifdef WSDISPLAY_COMPAT_RAWKBD
|
|
#include <dev/hpc/pckbd_encode.h>
|
|
#endif
|
|
|
|
#include <dev/hpc/hpckbdvar.h>
|
|
#include <dev/hpc/hpckbdkeymap.h>
|
|
|
|
struct hpckbd_softc;
|
|
|
|
#define NEVENTQ 32
|
|
struct hpckbd_eventq {
|
|
u_int hq_type;
|
|
int hq_data;
|
|
};
|
|
|
|
struct hpckbd_core {
|
|
struct hpckbd_if hc_if;
|
|
struct hpckbd_ic_if *hc_ic;
|
|
const u_int8_t *hc_keymap;
|
|
const int *hc_special;
|
|
int hc_polling;
|
|
int hc_console;
|
|
#define NEVENTQ 32
|
|
struct hpckbd_eventq hc_eventq[NEVENTQ];
|
|
struct hpckbd_eventq *hc_head, *hc_tail;
|
|
int hc_nevents;
|
|
int hc_enabled;
|
|
struct device *hc_wskbddev;
|
|
struct hpckbd_softc* hc_sc; /* back link */
|
|
#ifdef WSDISPLAY_COMPAT_RAWKBD
|
|
int hc_rawkbd;
|
|
#endif
|
|
};
|
|
|
|
struct hpckbd_softc {
|
|
struct device sc_dev;
|
|
struct hpckbd_core *sc_core;
|
|
struct hpckbd_core sc_coredata;
|
|
};
|
|
|
|
int hpckbd_match(struct device *, struct cfdata *, void *);
|
|
void hpckbd_attach(struct device *, struct device *, void *);
|
|
|
|
void hpckbd_initcore(struct hpckbd_core *, struct hpckbd_ic_if *, int);
|
|
void hpckbd_initif(struct hpckbd_core *);
|
|
int hpckbd_getevent(struct hpckbd_core *, u_int *, int *);
|
|
int hpckbd_putevent(struct hpckbd_core *, u_int, int);
|
|
void hpckbd_keymap_lookup(struct hpckbd_core*);
|
|
void hpckbd_keymap_setup(struct hpckbd_core *, const keysym_t *, int);
|
|
int __hpckbd_input(void *, int, int);
|
|
void __hpckbd_input_hook(void*);
|
|
|
|
CFATTACH_DECL(hpckbd, sizeof(struct hpckbd_softc),
|
|
hpckbd_match, hpckbd_attach, NULL, NULL);
|
|
|
|
/* wskbd accessopts */
|
|
int hpckbd_enable(void *, int);
|
|
void hpckbd_set_leds(void *, int);
|
|
int hpckbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
|
|
|
|
/* consopts */
|
|
struct hpckbd_core hpckbd_consdata;
|
|
void hpckbd_cngetc(void *, u_int *, int*);
|
|
void hpckbd_cnpollc(void *, int);
|
|
|
|
const struct wskbd_accessops hpckbd_accessops = {
|
|
hpckbd_enable,
|
|
hpckbd_set_leds,
|
|
hpckbd_ioctl,
|
|
};
|
|
|
|
const struct wskbd_consops hpckbd_consops = {
|
|
hpckbd_cngetc,
|
|
hpckbd_cnpollc,
|
|
};
|
|
|
|
struct wskbd_mapdata hpckbd_keymapdata = {
|
|
pckbd_keydesctab,
|
|
#ifdef PCKBD_LAYOUT
|
|
PCKBD_LAYOUT
|
|
#else
|
|
KB_US
|
|
#endif
|
|
};
|
|
|
|
int
|
|
hpckbd_match(struct device *parent, struct cfdata *cf, void *aux)
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
hpckbd_attach(struct device *parent, struct device *self, void *aux)
|
|
{
|
|
struct hpckbd_attach_args *haa = aux;
|
|
struct hpckbd_softc *sc = (void*)self;
|
|
struct hpckbd_ic_if *ic = haa->haa_ic;
|
|
struct wskbddev_attach_args wa;
|
|
|
|
/*
|
|
* Initialize core if it isn't console
|
|
*/
|
|
if (hpckbd_consdata.hc_ic == ic) {
|
|
sc->sc_core = &hpckbd_consdata;
|
|
/* The core has been initialized in hpckbd_cnattach. */
|
|
} else {
|
|
sc->sc_core = &sc->sc_coredata;
|
|
hpckbd_initcore(sc->sc_core, ic, 0 /* not console */);
|
|
}
|
|
|
|
if (sc->sc_core->hc_keymap == default_keymap)
|
|
printf(": no keymap.");
|
|
|
|
printf("\n");
|
|
|
|
/*
|
|
* setup hpckbd public interface for parent controller.
|
|
*/
|
|
hpckbd_initif(sc->sc_core);
|
|
|
|
/*
|
|
* attach wskbd
|
|
*/
|
|
wa.console = sc->sc_core->hc_console;
|
|
wa.keymap = &hpckbd_keymapdata;
|
|
wa.accessops = &hpckbd_accessops;
|
|
wa.accesscookie = sc->sc_core;
|
|
sc->sc_core->hc_wskbddev = config_found(self, &wa, wskbddevprint);
|
|
}
|
|
|
|
int
|
|
hpckbd_print(void *aux, const char *pnp)
|
|
{
|
|
return (pnp ? QUIET : UNCONF);
|
|
}
|
|
|
|
void
|
|
hpckbd_initcore(struct hpckbd_core *hc, struct hpckbd_ic_if *ic, int console)
|
|
{
|
|
hc->hc_polling = 0;
|
|
hc->hc_console = console;
|
|
hc->hc_ic = ic;
|
|
|
|
/* setup event queue */
|
|
hc->hc_head = hc->hc_tail = hc->hc_eventq;
|
|
hc->hc_nevents = 0;
|
|
|
|
hpckbd_keymap_lookup(hc);
|
|
}
|
|
|
|
void
|
|
hpckbd_initif(struct hpckbd_core *hc)
|
|
{
|
|
struct hpckbd_if *kbdif = &hc->hc_if;
|
|
|
|
kbdif->hi_ctx = hc;
|
|
kbdif->hi_input = __hpckbd_input;
|
|
kbdif->hi_input_hook = __hpckbd_input_hook;
|
|
hpckbd_ic_establish(hc->hc_ic, &hc->hc_if);
|
|
}
|
|
|
|
int
|
|
hpckbd_putevent(struct hpckbd_core* hc, u_int type, int data)
|
|
{
|
|
int s = spltty();
|
|
|
|
if (hc->hc_nevents == NEVENTQ) {
|
|
splx(s);
|
|
return (0); /* queue is full */
|
|
}
|
|
|
|
hc->hc_nevents++;
|
|
hc->hc_tail->hq_type = type;
|
|
hc->hc_tail->hq_data = data;
|
|
if (&hc->hc_eventq[NEVENTQ] <= ++hc->hc_tail)
|
|
hc->hc_tail = hc->hc_eventq;
|
|
splx(s);
|
|
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
hpckbd_getevent(struct hpckbd_core* hc, u_int *type, int *data)
|
|
{
|
|
int s = spltty();
|
|
|
|
if (hc->hc_nevents == 0) {
|
|
splx(s);
|
|
return (0); /* queue is empty */
|
|
}
|
|
|
|
*type = hc->hc_head->hq_type;
|
|
*data = hc->hc_head->hq_data;
|
|
hc->hc_nevents--;
|
|
if (&hc->hc_eventq[NEVENTQ] <= ++hc->hc_head)
|
|
hc->hc_head = hc->hc_eventq;
|
|
splx(s);
|
|
|
|
return (1);
|
|
}
|
|
|
|
void
|
|
hpckbd_keymap_setup(struct hpckbd_core *hc, const keysym_t *map, int mapsize)
|
|
{
|
|
int i;
|
|
struct wscons_keydesc *desc;
|
|
|
|
/* fix keydesc table */
|
|
desc = (struct wscons_keydesc *)hpckbd_keymapdata.keydesc;
|
|
for (i = 0; desc[i].name != 0; i++) {
|
|
if ((desc[i].name & KB_MACHDEP) && desc[i].map == NULL) {
|
|
desc[i].map = map;
|
|
desc[i].map_size = mapsize;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hpckbd_keymap_lookup(struct hpckbd_core *hc)
|
|
{
|
|
const struct hpckbd_keymap_table *tab;
|
|
platid_mask_t mask;
|
|
|
|
for (tab = hpckbd_keymap_table; tab->ht_platform != NULL; tab++) {
|
|
|
|
mask = PLATID_DEREF(tab->ht_platform);
|
|
|
|
if (platid_match(&platid, &mask)) {
|
|
hc->hc_keymap = tab->ht_keymap;
|
|
hc->hc_special = tab->ht_special;
|
|
#if !defined(PCKBD_LAYOUT)
|
|
hpckbd_keymapdata.layout = tab->ht_layout;
|
|
#endif
|
|
if (tab->ht_cmdmap.map) {
|
|
hpckbd_keymap_setup(hc, tab->ht_cmdmap.map,
|
|
tab->ht_cmdmap.size);
|
|
#if !defined(PCKBD_LAYOUT)
|
|
hpckbd_keymapdata.layout |= KB_MACHDEP;
|
|
#endif
|
|
} else {
|
|
hpckbd_keymapdata.layout &= ~KB_MACHDEP;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* no keymap. use default. */
|
|
hc->hc_keymap = default_keymap;
|
|
hc->hc_special = default_special_keymap;
|
|
#if !defined(PCKBD_LAYOUT)
|
|
hpckbd_keymapdata.layout = KB_US;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
__hpckbd_input_hook(void *arg)
|
|
{
|
|
#if 0
|
|
struct hpckbd_core *hc = arg;
|
|
|
|
if (hc->hc_polling) {
|
|
hc->hc_type = WSCONS_EVENT_ALL_KEYS_UP;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int
|
|
__hpckbd_input(void *arg, int flag, int scancode)
|
|
{
|
|
struct hpckbd_core *hc = arg;
|
|
int type, key;
|
|
|
|
if (flag) {
|
|
type = WSCONS_EVENT_KEY_DOWN;
|
|
} else {
|
|
type = WSCONS_EVENT_KEY_UP;
|
|
}
|
|
|
|
if ((key = hc->hc_keymap[scancode]) == UNK) {
|
|
#ifdef DEBUG
|
|
printf("hpckbd: unknown scan code %#x (%d, %d)\n",
|
|
scancode, scancode >> 3,
|
|
scancode - ((scancode >> 3) << 3));
|
|
#endif /* DEBUG */
|
|
return (0);
|
|
}
|
|
|
|
if (key == IGN) {
|
|
return (0);
|
|
}
|
|
|
|
if (key == SPL) {
|
|
if (!flag)
|
|
return (0);
|
|
|
|
if (scancode == hc->hc_special[KEY_SPECIAL_OFF]) {
|
|
#ifdef DEBUG
|
|
printf("off button\n"); // XXX notyet -uch
|
|
#endif
|
|
} else if (scancode == hc->hc_special[KEY_SPECIAL_LIGHT]) {
|
|
static int onoff; /* XXX -uch */
|
|
config_hook_call(CONFIG_HOOK_BUTTONEVENT,
|
|
CONFIG_HOOK_BUTTONEVENT_LIGHT,
|
|
(void *)(onoff ^= 1));
|
|
} else {
|
|
#ifdef DEBUG
|
|
printf("unknown special key %d\n", scancode);
|
|
#endif
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
if (hc->hc_polling) {
|
|
if (hpckbd_putevent(hc, type, hc->hc_keymap[scancode]) == 0)
|
|
printf("hpckbd: queue over flow");
|
|
} else {
|
|
#ifdef WSDISPLAY_COMPAT_RAWKBD
|
|
if (hc->hc_rawkbd) {
|
|
int n;
|
|
u_char data[16];
|
|
n = pckbd_encode(type, hc->hc_keymap[scancode], data);
|
|
wskbd_rawinput(hc->hc_wskbddev, data, n);
|
|
} else
|
|
#endif
|
|
wskbd_input(hc->hc_wskbddev, type, hc->hc_keymap[scancode]);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* console support routines
|
|
*/
|
|
int
|
|
hpckbd_cnattach(struct hpckbd_ic_if *ic)
|
|
{
|
|
struct hpckbd_core *hc = &hpckbd_consdata;
|
|
|
|
hpckbd_initcore(hc, ic, 1 /* console */);
|
|
|
|
/* attach controller */
|
|
hpckbd_initif(hc);
|
|
|
|
/* attach wskbd */
|
|
wskbd_cnattach(&hpckbd_consops, hc, &hpckbd_keymapdata);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
hpckbd_cngetc(void *arg, u_int *type, int *data)
|
|
{
|
|
struct hpckbd_core *hc = arg;
|
|
|
|
if (!hc->hc_console || !hc->hc_polling || !hc->hc_ic)
|
|
return;
|
|
|
|
while (hpckbd_getevent(hc, type, data) == 0) /* busy loop */
|
|
hpckbd_ic_poll(hc->hc_ic);
|
|
}
|
|
|
|
void
|
|
hpckbd_cnpollc(void *arg, int on)
|
|
{
|
|
struct hpckbd_core *hc = arg;
|
|
|
|
hc->hc_polling = on;
|
|
}
|
|
|
|
int
|
|
hpckbd_enable(void *arg, int on)
|
|
{
|
|
struct hpckbd_core *hc = arg;
|
|
|
|
if (on) {
|
|
if (hc->hc_enabled)
|
|
return (EBUSY);
|
|
hc->hc_enabled = 1;
|
|
} else {
|
|
if (hc->hc_console)
|
|
return (EBUSY);
|
|
hc->hc_enabled = 0;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
hpckbd_set_leds(void *arg, int leds)
|
|
{
|
|
/* Can you find any LED which tells you about keyboard? */
|
|
}
|
|
|
|
int
|
|
hpckbd_ioctl(void *arg, u_long cmd, caddr_t data, int flag, struct proc *p)
|
|
{
|
|
#ifdef WSDISPLAY_COMPAT_RAWKBD
|
|
struct hpckbd_core *hc = arg;
|
|
#endif
|
|
switch (cmd) {
|
|
case WSKBDIO_GTYPE:
|
|
*(int *)data = WSKBD_TYPE_HPC_KBD;
|
|
return (0);
|
|
case WSKBDIO_SETLEDS:
|
|
return 0;
|
|
case WSKBDIO_GETLEDS:
|
|
*(int *)data = 0; /* dummy for wsconsctl(8) */
|
|
return (0);
|
|
#ifdef WSDISPLAY_COMPAT_RAWKBD
|
|
case WSKBDIO_SETMODE:
|
|
hc->hc_rawkbd = (*(int *)data == WSKBD_RAW);
|
|
return (0);
|
|
#endif
|
|
}
|
|
return (EPASSTHROUGH);
|
|
}
|