diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2003-01-31 22:50:20 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2003-01-31 22:50:20 +0000 |
commit | 06cc3bda31bd3d5f24d4ca8606317cf1ae361230 (patch) | |
tree | 3bd75b44de4c04b9c978382b04502970d240488f /sys/arch/hppa/gsc | |
parent | 588475d783a52d61a2bf8807a16cbba243f246d1 (diff) |
Enter gsckbc, a driver for the ps/2-like input device ports found on many
hppa machines, and gsckbd, a driver for the ps/2-like keyboard.
Both are derived from pckbc and pckbd, but the differences in port wiring and
keyboard behaviour prevented the creation of a thin pckbc wrapper.
Currently, only us and uk keyboard layouts are provided.
The adventurous person can enable gsckb[cd], wsdisplay at sti and wskbd,
but this will only work correctly on machines featuring a modern-prom sti
graphics device, and a ps/2 keyboard port.
Diffstat (limited to 'sys/arch/hppa/gsc')
-rw-r--r-- | sys/arch/hppa/gsc/gsckbc.c | 1019 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/gsckbcreg.h | 64 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/gsckbd.c | 607 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/gsckbdmap.c | 169 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/gsckbdmap.h | 29 | ||||
-rw-r--r-- | sys/arch/hppa/gsc/gsckbdvar.h | 29 |
6 files changed, 1917 insertions, 0 deletions
diff --git a/sys/arch/hppa/gsc/gsckbc.c b/sys/arch/hppa/gsc/gsckbc.c new file mode 100644 index 00000000000..f598a3e8427 --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbc.c @@ -0,0 +1,1019 @@ +/* $OpenBSD: gsckbc.c,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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 MIND, + * 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. + */ + +/* + * Derived from /sys/dev/ic/pckbd.c under the following terms: + * OpenBSD: pckbc.c,v 1.5 2002/06/09 00:58:03 nordin Exp + * NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp + */ +/* + * Copyright (c) 1998 + * Matthias Drochner. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Matthias Drochner. + * 4. 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. + */ + +/* + * Driver for the PS/2-like keyboard and mouse ports found on 712 and 715 + * models, among others. + * + * Contrary to the ``pckbc'' port set found on other arches, the + * keyboard and mouse port are two separate entities on the snakes, and + * they are driven by a custom chip not 8042-compatible. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/proc.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/autoconf.h> + +#include <hppa/dev/cpudevs.h> +#include <hppa/gsc/gscbusvar.h> + +#include <hppa/gsc/gsckbcreg.h> +#include <dev/ic/pckbcvar.h> + +#include <dev/pckbc/pckbdreg.h> /* constants for probe magic */ +#include <hppa/gsc/gsckbdvar.h> + +int gsckbc_match(struct device *, void *, void *); +void gsckbc_attach(struct device *, struct device *, void *); + +struct gsckbc_softc { + struct pckbc_softc sc_pckbc; + + int sc_irq; + void *sc_ih; + int sc_type; +}; + +struct cfattach gsckbc_ca = { + sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach +}; + +struct cfdriver gsckbc_cd = { + NULL, "gsckbc", DV_DULL +}; + +void gsckbc_intr_establish(struct pckbc_softc *, pckbc_slot_t); + +#include "gsckbd.h" +#if (NGSCKBD > 0) +#include <dev/pckbc/pckbdvar.h> +#endif + +/* descriptor for one device command */ +struct pckbc_devcmd { + TAILQ_ENTRY(pckbc_devcmd) next; + int flags; +#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */ +#define KBC_CMDFLAG_SLOW 2 + u_char cmd[4]; + int cmdlen, cmdidx, retries; + u_char response[4]; + int status, responselen, responseidx; +}; + +/* data per slave device */ +struct pckbc_slotdata { + int polling; /* don't read data port in interrupt handler */ + TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */ + TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */ +#define NCMD 5 + struct pckbc_devcmd cmds[NCMD]; +}; + +#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL) +/* Force polling mode behaviour for boot -a XXX */ +#define IS_POLLING(q) ((q)->polling || cold) + +void pckbc_init_slotdata(struct pckbc_slotdata *); +int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t); +int pckbc_submatch(struct device *, void *, void *); +int pckbcprint(void *, const char *); + +int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t); +int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t, + u_char); +void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t, + struct pckbc_devcmd *); + +void pckbc_cleanqueue(struct pckbc_slotdata *); +void pckbc_cleanup(void *); +int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char); +void pckbc_start(struct pckbc_internal *, pckbc_slot_t); + +const char *pckbc_slot_names[] = { "kbd", "mouse" }; + +#define KBC_DEVCMD_ACK 0xfa +#define KBC_DEVCMD_RESEND 0xfe + +#define KBD_DELAY DELAY(8) + +int +gsckbc_match(struct device *parent, void *match, void *aux) +{ + struct gsc_attach_args *ga = aux; + bus_space_handle_t ioh; + u_int8_t rv; + + if (ga->ga_type.iodc_type != HPPA_TYPE_FIO || + ga->ga_type.iodc_sv_model != HPPA_FIO_GPCIO) + return (0); + + /* Map the i/o space. */ + if (bus_space_map(ga->ga_ca.ca_iot, ga->ga_ca.ca_hpa, + KBMAPSIZE, 0, &ioh)) + return 0; + + rv = bus_space_read_1(ga->ga_ca.ca_iot, ioh, KBIDP); + bus_space_unmap(ga->ga_ca.ca_iot, ioh, KBMAPSIZE); + + if (rv == PCKBC_KBD_SLOT || rv == PCKBC_AUX_SLOT) + return (1); /* keyboard or mouse port */ + + return (0); +} + +/* + * Attachment helper functions + */ + +/* state machine values */ +#define PROBE_SUCCESS 0 +#define PROBE_TIMEOUT 1 +#define PROBE_RETRANS 2 +#define PROBE_NOACK 3 + +int probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply); +int probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply); +int probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte); +int probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte); +int probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh); + +#define PROBE_TRIES 1000 + +int +probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply) +{ + int numtries = PROBE_TRIES; + + while (numtries--) { + if (bus_space_read_1(iot, ioh, KBSTATP) & + (KBS_DIB | KBS_TERR | KBS_PERR)) + break; + DELAY(500); + } + + if (numtries <= 0) + return (PROBE_TIMEOUT); + + if (bus_space_read_1(iot, ioh, KBSTATP) & (KBS_PERR | KBS_TERR)) { + if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) { + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + return (PROBE_TIMEOUT); + } + + *reply = bus_space_read_1(iot, ioh, KBDATAP); + if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) { + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + if (probe_sendtmo(iot, ioh, KBR_RESEND)) + return (PROBE_TIMEOUT); + else + return (PROBE_RETRANS); + } else + return (PROBE_SUCCESS); + } else { + *reply = bus_space_read_1(iot, ioh, KBDATAP); + return (PROBE_SUCCESS); + } +} + +int +probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply) +{ + int read_status; + int retrans = KB_MAX_RETRANS; + + do { + read_status = probe_readtmo(iot, ioh, reply); + } while ((read_status == PROBE_RETRANS) && retrans--); + + return (read_status); +} + +int +probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte) +{ + int numtries = PROBE_TRIES; + + while (numtries--) { + if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) == 0) + break; + DELAY(500); + } + + if (bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) + return (1); + + bus_space_write_1(iot, ioh, KBDATAP, cmdbyte); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + return (0); +} + +int +probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte) +{ + int retranscount; + int reply; + + for (retranscount = 0; retranscount < KB_MAX_RETRANS; retranscount++) { + if (probe_sendtmo(iot, ioh, cmdbyte)) + return (PROBE_TIMEOUT); + if (probe_readretry(iot, ioh, &reply)) + return (PROBE_TIMEOUT); + + switch (reply) { + case KBR_ACK: + return (PROBE_SUCCESS); + case KBR_RESEND: + break; + default: + return (PROBE_NOACK); + } + } + return (PROBE_TIMEOUT); + +} + +int +probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh) +{ + int status; + + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + DELAY(0x20000); /* XXX why 0x? */ + bus_space_write_1(iot, ioh, KBCMDP, 0); + DELAY(20000); + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_DIAG); + DELAY(20000); + + status = probe_sendack(iot, ioh, KBC_DISABLE); + switch (status) { + case PROBE_TIMEOUT: + if (bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) { + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + } + return (-1); + case PROBE_NOACK: + return (-1); + } + + if (probe_sendack(iot, ioh, KBC_ID) != PROBE_SUCCESS) + return (-1); + + if (probe_readretry(iot, ioh, &status)) + return (-1); + + switch (status) { + case KBR_MOUSE_ID: + return PCKBC_AUX_SLOT; + case KBR_KBD_ID1: + if (probe_readretry(iot, ioh, &status)) + return (-1); + if (status == KBR_KBD_ID2) { + if (probe_sendack(iot, ioh, KBC_ENABLE) == + PROBE_TIMEOUT) { + bus_space_write_1(iot, ioh, KBRESETP, 0); + bus_space_write_1(iot, ioh, KBCMDP, + KBCP_ENABLE); + } + return PCKBC_KBD_SLOT; + } + } + return (-1); +} + +void +gsckbc_attach(struct device *parent, struct device *self, void *aux) +{ + struct gsc_attach_args *ga = aux; + struct gsckbc_softc *gsc = (void *)self; + struct pckbc_softc *sc = &gsc->sc_pckbc; + struct pckbc_internal *t; + bus_space_tag_t iot; + bus_space_handle_t ioh; + int ident; + + iot = ga->ga_ca.ca_iot; + gsc->sc_irq = ga->ga_ca.ca_irq; + + if (bus_space_map(iot, ga->ga_ca.ca_hpa, KBMAPSIZE, 0, &ioh)) + panic("gsckbc_attach: couldn't map port"); + + gsc->sc_type = bus_space_read_1(iot, ioh, KBIDP); + + switch (gsc->sc_type) { + case PCKBC_KBD_SLOT: + case PCKBC_AUX_SLOT: + break; + default: + printf(": unknown port type %x\n", gsc->sc_type); + /* play nice and don't really attach. */ + bus_space_unmap(iot, ioh, KBMAPSIZE); + return; + } + + sc->intr_establish = gsckbc_intr_establish; + + t = malloc(sizeof(struct pckbc_internal), M_DEVBUF, M_WAITOK); + bzero(t, sizeof(struct pckbc_internal)); + t->t_iot = iot; + /* XXX it does not make sense to only map two ports here */ + t->t_ioh_d = t->t_ioh_c = ioh; + t->t_addr = ga->ga_ca.ca_hpa; + t->t_sc = sc; + timeout_set(&t->t_cleanup, pckbc_cleanup, t); + sc->id = t; + + printf("\n"); + + /* + * Reset port and probe device, if plugged + */ + ident = probe_ident(iot, ioh); + if (ident != gsc->sc_type) { + /* don't whine for unplugged ports */ + if (ident != -1) + printf("%s: expecting device type %d, got %d\n", + sc->sc_dv.dv_xname, gsc->sc_type, ident); + } else { +#if (NGSCKBD > 0) + if (gsc->sc_type == PCKBC_KBD_SLOT) + gsckbd_cnattach(t, PCKBC_KBD_SLOT); +#endif + pckbc_attach_slot(sc, gsc->sc_type); + } +} + +void +gsckbc_intr_establish(struct pckbc_softc *sc, pckbc_slot_t slot) +{ + struct gsckbc_softc *gsc = (void *)sc; + + gsc->sc_ih = gsc_intr_establish( + (struct gsc_softc *)sc->sc_dv.dv_parent, + IPL_TTY, gsc->sc_irq, pckbcintr, sc, &sc->sc_dv); +} + +/* + * pckbc-like interfaces + */ + +int +pckbc_wait_output(iot, ioh) + bus_space_tag_t iot; + bus_space_handle_t ioh; +{ + u_int i; + + for (i = 100000; i; i--) { + if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD)) { + KBD_DELAY; + } else + return (1); + } + return (0); +} + +int +pckbc_send_cmd(iot, ioh, val) + bus_space_tag_t iot; + bus_space_handle_t ioh; + u_char val; +{ + if (!pckbc_wait_output(iot, ioh)) + return (0); + bus_space_write_1(iot, ioh, KBOUTP, val); + bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE); + return (1); +} + +/* XXX logic */ +int +pckbc_poll_data1(iot, ioh, ioh_c, slot, checkaux) + bus_space_tag_t iot; + bus_space_handle_t ioh, ioh_c; + pckbc_slot_t slot; + int checkaux; /* ignored on hppa */ +{ + int i; + u_char stat; + + /* if 1 port read takes 1us (?), this polls for 100ms */ + for (i = 100000; i; i--) { + stat = bus_space_read_1(iot, ioh, KBSTATP); + if (stat & KBS_DIB) { + KBD_DELAY; + return bus_space_read_1(iot, ioh, KBDATAP); + } + } + return (-1); +} + +int +pckbc_send_devcmd(t, slot, val) + struct pckbc_internal *t; + pckbc_slot_t slot; + u_char val; +{ + return pckbc_send_cmd(t->t_iot, t->t_ioh_d, val); +} + +int +pckbc_submatch(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct pckbc_attach_args *pa = aux; + + if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT && + cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot) + return (0); + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +int +pckbc_attach_slot(sc, slot) + struct pckbc_softc *sc; + pckbc_slot_t slot; +{ + struct pckbc_internal *t = sc->id; + struct pckbc_attach_args pa; + int found; + + pa.pa_tag = t; + pa.pa_slot = slot; + found = (config_found_sm((struct device *)sc, &pa, + pckbcprint, pckbc_submatch) != NULL); + + if (found && !t->t_slotdata[slot]) { + t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata), + M_DEVBUF, M_NOWAIT); + if (t->t_slotdata[slot] == NULL) + return 0; + pckbc_init_slotdata(t->t_slotdata[slot]); + } + return (found); +} + +int +pckbcprint(aux, pnp) + void *aux; + const char *pnp; +{ +#if 0 /* hppa having devices for each slot, this is barely useful */ + struct pckbc_attach_args *pa = aux; + + if (!pnp) + printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]); +#endif + return (QUIET); +} + +void +pckbc_init_slotdata(q) + struct pckbc_slotdata *q; +{ + int i; + TAILQ_INIT(&q->cmdqueue); + TAILQ_INIT(&q->freequeue); + + for (i = 0; i < NCMD; i++) { + TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next); + } + q->polling = 0; +} + +void +pckbc_flush(self, slot) + pckbc_tag_t self; + pckbc_slot_t slot; +{ + struct pckbc_internal *t = self; + + pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0); +} + +int +pckbc_poll_data(self, slot) + pckbc_tag_t self; + pckbc_slot_t slot; +{ + struct pckbc_internal *t = self; + struct pckbc_slotdata *q = t->t_slotdata[slot]; + int c; + + c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0); + if (c != -1 && q && CMD_IN_QUEUE(q)) { + /* we jumped into a running command - try to + deliver the response */ + if (pckbc_cmdresponse(t, slot, c)) + return (-1); + } + return (c); +} + +void +pckbc_slot_enable(self, slot, on) + pckbc_tag_t self; + pckbc_slot_t slot; + int on; +{ + /* can't enable slots here as they are different devices */ +} + +void +pckbc_set_poll(self, slot, on) + pckbc_tag_t self; + pckbc_slot_t slot; + int on; +{ + struct pckbc_internal *t = (struct pckbc_internal *)self; + + t->t_slotdata[slot]->polling = on; + + if (!on) { + int s; + + /* + * If disabling polling on a device that's been configured, + * make sure there are no bytes left in the FIFO, holding up + * the interrupt line. Otherwise we won't get any further + * interrupts. + */ + if (t->t_sc) { + s = spltty(); + pckbcintr(t->t_sc); + splx(s); + } + } +} + +/* + * Pass command to device, poll for ACK and data. + * to be called at spltty() + */ +void +pckbc_poll_cmd1(t, slot, cmd) + struct pckbc_internal *t; + pckbc_slot_t slot; + struct pckbc_devcmd *cmd; +{ + bus_space_tag_t iot = t->t_iot; + bus_space_handle_t ioh = t->t_ioh_d; + int i, c = 0; + + while (cmd->cmdidx < cmd->cmdlen) { + if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) { + printf("pckbc_cmd: send error\n"); + cmd->status = EIO; + return; + } + for (i = 10; i; i--) { /* 1s ??? */ + c = pckbc_poll_data1(iot, ioh, ioh, slot, 0); + if (c != -1) + break; + } + + if (c == KBC_DEVCMD_ACK) { + cmd->cmdidx++; + continue; + } + if (c == KBC_DEVCMD_RESEND) { +#ifdef PCKBCDEBUG + printf("pckbc_cmd: RESEND\n"); +#endif + if (cmd->retries++ < KB_MAX_RETRANS) + continue; + else { +#ifdef PCKBCDEBUG + printf("pckbc: cmd failed\n"); +#endif + cmd->status = EIO; + return; + } + } + if (c == -1) { +#ifdef PCKBCDEBUG + printf("pckbc_cmd: timeout\n"); +#endif + cmd->status = EIO; + return; + } +#ifdef PCKBCDEBUG + printf("pckbc_cmd: lost 0x%x\n", c); +#endif + } + + while (cmd->responseidx < cmd->responselen) { + if (cmd->flags & KBC_CMDFLAG_SLOW) + i = 100; /* 10s ??? */ + else + i = 10; /* 1s ??? */ + while (i--) { + c = pckbc_poll_data1(iot, ioh, ioh, slot, 0); + if (c != -1) + break; + } + if (c == -1) { +#ifdef PCKBCDEBUG + printf("pckbc_cmd: no data\n"); +#endif + cmd->status = ETIMEDOUT; + return; + } else + cmd->response[cmd->responseidx++] = c; + } +} + +/* for use in autoconfiguration */ +int +pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow) + pckbc_tag_t self; + pckbc_slot_t slot; + u_char *cmd; + int len, responselen; + u_char *respbuf; + int slow; +{ + struct pckbc_internal *t = self; + struct pckbc_devcmd nc; + + if ((len > 4) || (responselen > 4)) + return (EINVAL); + + bzero(&nc, sizeof(nc)); + bcopy(cmd, nc.cmd, len); + nc.cmdlen = len; + nc.responselen = responselen; + nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0); + + pckbc_poll_cmd1(t, slot, &nc); + + if (nc.status == 0 && respbuf) + bcopy(nc.response, respbuf, responselen); + + return (nc.status); +} + +/* + * Clean up a command queue, throw away everything. + */ +void +pckbc_cleanqueue(q) + struct pckbc_slotdata *q; +{ + struct pckbc_devcmd *cmd; +#ifdef PCKBCDEBUG + int i; +#endif + + while ((cmd = TAILQ_FIRST(&q->cmdqueue))) { + TAILQ_REMOVE(&q->cmdqueue, cmd, next); +#ifdef PCKBCDEBUG + printf("pckbc_cleanqueue: removing"); + for (i = 0; i < cmd->cmdlen; i++) + printf(" %02x", cmd->cmd[i]); + printf("\n"); +#endif + TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); + } +} + +/* + * Timeout error handler: clean queues and data port. + * XXX could be less invasive. + */ +void +pckbc_cleanup(self) + void *self; +{ + struct pckbc_internal *t = self; + int s; + + printf("pckbc: command timeout\n"); + + s = spltty(); + + if (t->t_slotdata[PCKBC_KBD_SLOT]) + pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]); + if (t->t_slotdata[PCKBC_AUX_SLOT]) + pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]); + + while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) { + KBD_DELAY; + (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP); + } + + /* reset KBC? */ + + splx(s); +} + +/* + * Pass command to device during normal operation. + * to be called at spltty() + */ +void +pckbc_start(t, slot) + struct pckbc_internal *t; + pckbc_slot_t slot; +{ + struct pckbc_slotdata *q = t->t_slotdata[slot]; + struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue); + + if (IS_POLLING(q)) { + do { + pckbc_poll_cmd1(t, slot, cmd); + if (cmd->status) + printf("pckbc_start: command error\n"); + + TAILQ_REMOVE(&q->cmdqueue, cmd, next); + if (cmd->flags & KBC_CMDFLAG_SYNC) + wakeup(cmd); + else { + timeout_del(&t->t_cleanup); + TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); + } + cmd = TAILQ_FIRST(&q->cmdqueue); + } while (cmd); + return; + } + + if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) { + printf("pckbc_start: send error\n"); + /* XXX what now? */ + return; + } +} + +/* + * Handle command responses coming in asynchonously, + * return nonzero if valid response. + * to be called at spltty() + */ +int +pckbc_cmdresponse(t, slot, data) + struct pckbc_internal *t; + pckbc_slot_t slot; + u_char data; +{ + struct pckbc_slotdata *q = t->t_slotdata[slot]; + struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue); + +#ifdef DIAGNOSTIC + if (!cmd) + panic("pckbc_cmdresponse: no active command"); +#endif + if (cmd->cmdidx < cmd->cmdlen) { + if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND) + return (0); + + if (data == KBC_DEVCMD_RESEND) { + if (cmd->retries++ < KB_MAX_RETRANS) { + /* try again last command */ + goto restart; + } else { + printf("pckbc: cmd failed\n"); + cmd->status = EIO; + /* dequeue */ + } + } else { + if (++cmd->cmdidx < cmd->cmdlen) + goto restart; + if (cmd->responselen) + return (1); + /* else dequeue */ + } + } else if (cmd->responseidx < cmd->responselen) { + cmd->response[cmd->responseidx++] = data; + if (cmd->responseidx < cmd->responselen) + return (1); + /* else dequeue */ + } else + return (0); + + /* dequeue: */ + TAILQ_REMOVE(&q->cmdqueue, cmd, next); + if (cmd->flags & KBC_CMDFLAG_SYNC) + wakeup(cmd); + else { + timeout_del(&t->t_cleanup); + TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); + } + if (!CMD_IN_QUEUE(q)) + return (1); +restart: + pckbc_start(t, slot); + return (1); +} + +/* + * Put command into the device's command queue, return zero or errno. + */ +int +pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf) + pckbc_tag_t self; + pckbc_slot_t slot; + u_char *cmd; + int len, responselen, sync; + u_char *respbuf; +{ + struct pckbc_internal *t = self; + struct pckbc_slotdata *q = t->t_slotdata[slot]; + struct pckbc_devcmd *nc; + int s, isactive, res = 0; + + if ((len > 4) || (responselen > 4)) + return (EINVAL); + s = spltty(); + nc = TAILQ_FIRST(&q->freequeue); + if (nc) { + TAILQ_REMOVE(&q->freequeue, nc, next); + } + splx(s); + if (!nc) + return (ENOMEM); + + bzero(nc, sizeof(*nc)); + bcopy(cmd, nc->cmd, len); + nc->cmdlen = len; + nc->responselen = responselen; + nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0); + + s = spltty(); + + if (IS_POLLING(q) && sync) { + /* + * XXX We should poll until the queue is empty. + * But we don't come here normally, so make + * it simple and throw away everything. + */ + pckbc_cleanqueue(q); + } + + isactive = CMD_IN_QUEUE(q); + TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next); + if (!isactive) + pckbc_start(t, slot); + + if (IS_POLLING(q)) + res = (sync ? nc->status : 0); + else if (sync) { + if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) { + TAILQ_REMOVE(&q->cmdqueue, nc, next); + pckbc_cleanup(t); + } else + res = nc->status; + } else + timeout_add(&t->t_cleanup, hz); + + if (sync) { + if (respbuf) + bcopy(nc->response, respbuf, responselen); + TAILQ_INSERT_TAIL(&q->freequeue, nc, next); + } + + splx(s); + + return (res); +} + +void +pckbc_set_inputhandler(self, slot, func, arg, name) + pckbc_tag_t self; + pckbc_slot_t slot; + pckbc_inputfcn func; + void *arg; + char *name; +{ + struct pckbc_internal *t = (struct pckbc_internal *)self; + struct pckbc_softc *sc = t->t_sc; + + if (slot >= PCKBC_NSLOTS) + panic("pckbc_set_inputhandler: bad slot %d", slot); + + (*sc->intr_establish)(sc, slot); + + sc->inputhandler[slot] = func; + sc->inputarg[slot] = arg; + sc->subname[slot] = name; +} + +int +pckbcintr(vsc) + void *vsc; +{ + struct pckbc_softc *sc = (struct pckbc_softc *)vsc; + struct gsckbc_softc *gsc = (void *)sc; + struct pckbc_internal *t = sc->id; + pckbc_slot_t slot; + struct pckbc_slotdata *q; + int served = 0, data; + + while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) { + served = 1; + + slot = gsc->sc_type; + q = t->t_slotdata[slot]; + + if (!q) { + /* XXX do something for live insertion? */ +#ifdef PCKBCDEBUG + printf("pckbcintr: no dev for slot %d\n", slot); +#endif + KBD_DELAY; + (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP); + continue; + } + + if (IS_POLLING(q)) + break; /* pckbc_poll_data() will get it */ + + KBD_DELAY; + data = bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP); + + if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data)) + continue; + + if (sc->inputhandler[slot]) + (*sc->inputhandler[slot])(sc->inputarg[slot], data); +#ifdef PCKBCDEBUG + else + printf("pckbcintr: slot %d lost %d\n", slot, data); +#endif + } + + return (served); +} diff --git a/sys/arch/hppa/gsc/gsckbcreg.h b/sys/arch/hppa/gsc/gsckbcreg.h new file mode 100644 index 00000000000..b682f98a536 --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbcreg.h @@ -0,0 +1,64 @@ +/* $OpenBSD: gsckbcreg.h,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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 MIND, + * 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. + */ + +/* + * Register definitions for the GSC PS/2 compatible keyboard/mouse ports. + * + * These definitions attempt to match <dev/ic/i8042reg.h> names although the + * actual wiring is different. + */ + +#define KBSTATP 12 /* controller status port (I) */ +#define KBS_DIB 0x01 /* data in buffer */ +#define KBS_OCMD 0x02 /* output buffer has command */ +#define KBS_PERR 0x04 /* parity error */ +#define KBS_TERR 0x08 /* transmission error */ + +#define KBCMDP 8 /* controller port (O) */ +#define KBCP_ENABLE 0x01 /* enable device */ +#define KBCP_DIAG 0x20 /* diagnostic mode control */ + +#define KBDATAP 4 /* data port (I) */ +#define KBOUTP 4 /* data port (O) */ + +#define KBIDP 0 /* id port (I) */ +#define ID_KBD 0 /* slot is a keyboard port */ +#define ID_MOUSE 1 /* slot is a mouse port */ + +#define KBRESETP 0 /* reset port (O) */ + +#define KBMAPSIZE 16 /* size to bus_space_map() */ + +/* + * Various command definitions not provided by the existing pckbc code. + */ + +#define KBC_ID 0xF2 /* get device identifier */ +#define KBR_MOUSE_ID 0x00 /* mouse type */ +#define KBR_KBD_ID1 0xAB /* keyboard type */ +#define KBR_KBD_ID2 0x83 + +#define KB_MAX_RETRANS 5 /* maximum number of command retrans attempts */ diff --git a/sys/arch/hppa/gsc/gsckbd.c b/sys/arch/hppa/gsc/gsckbd.c new file mode 100644 index 00000000000..515983fde8d --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbd.c @@ -0,0 +1,607 @@ +/* $OpenBSD: gsckbd.c,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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. + * + */ + +/* + * Derived from /sys/dev/pckbc/pckbd.c under the following terms: + * OpenBSD: pckbd.c,v 1.4 2002/03/14 01:27:00 millert Exp + * NetBSD: pckbd.c,v 1.24 2000/06/05 22:20:57 sommerfeld Exp + */ +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * 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. + */ + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * 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. + * + * @(#)pccons.c 5.11 (Berkeley) 5/21/91 + */ + +/* + * A pckbd-like driver for the GSC keyboards found on various HP workstations. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/ioctl.h> + +#include <machine/bus.h> + +#include <dev/ic/pckbcvar.h> + +#include <dev/pckbc/pckbdreg.h> +#include <hppa/gsc/gsckbdvar.h> +#include <hppa/gsc/gsckbdmap.h> + +#include <dev/wscons/wsconsio.h> +#include <dev/wscons/wskbdvar.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> + +struct gsckbd_internal { + int t_isconsole; + pckbc_tag_t t_kbctag; + pckbc_slot_t t_kbcslot; + + int t_lastchar; + int t_extended; + int t_releasing; + int t_extended1; + + struct gsckbd_softc *t_sc; /* back pointer */ +}; + +struct gsckbd_softc { + struct device sc_dev; + + struct gsckbd_internal *id; + int sc_enabled; + + int sc_ledstate; + + struct device *sc_wskbddev; +#ifdef WSDISPLAY_COMPAT_RAWKBD + int rawkbd; +#endif +}; + +static int gsckbd_is_console(pckbc_tag_t, pckbc_slot_t); + +int gsckbdprobe(struct device *, void *, void *); +void gsckbdattach(struct device *, struct device *, void *); + +struct cfdriver gsckbd_cd = { + NULL, "gsckbd", DV_DULL +}; + +struct cfattach gsckbd_ca = { + sizeof(struct gsckbd_softc), gsckbdprobe, gsckbdattach, +}; + +int gsckbd_enable(void *, int); +void gsckbd_set_leds(void *, int); +int gsckbd_ioctl(void *, u_long, caddr_t, int, struct proc *); + +const struct wskbd_accessops gsckbd_accessops = { + gsckbd_enable, + gsckbd_set_leds, + gsckbd_ioctl, +}; + +void gsckbd_cngetc(void *, u_int *, int *); +void gsckbd_cnpollc(void *, int); + +const struct wskbd_consops gsckbd_consops = { + gsckbd_cngetc, + gsckbd_cnpollc, + NULL +}; + +const struct wskbd_mapdata gsckbd_keymapdata = { + gsckbd_keydesctab, /* XXX */ +#ifdef GSCKBD_LAYOUT + GSCKBD_LAYOUT, +#else + KB_US, +#endif +}; + +int gsckbd_init(struct gsckbd_internal *, pckbc_tag_t, pckbc_slot_t, + int); +void gsckbd_input(void *, int); + +static int gsckbd_decode(struct gsckbd_internal *, int, + u_int *, int *); +static int gsckbd_led_encode(int); +static int gsckbd_led_decode(int); + +struct gsckbd_internal gsckbd_consdata; + +static int +gsckbd_is_console(tag, slot) + pckbc_tag_t tag; + pckbc_slot_t slot; +{ + return (gsckbd_consdata.t_isconsole && + (tag == gsckbd_consdata.t_kbctag) && + (slot == gsckbd_consdata.t_kbcslot)); +} + +/* + * these are both EXTREMELY bad jokes + */ +int +gsckbdprobe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct cfdata *cf = match; + struct pckbc_attach_args *pa = aux; + u_char cmd[1], resp[1]; + int res; + + /* + * XXX There are rumours that a keyboard can be connected + * to the aux port as well. For me, this didn't work. + * For further experiments, allow it if explicitly + * wired in the config file. + */ + if ((pa->pa_slot != PCKBC_KBD_SLOT) && + (cf->cf_loc[PCKBCCF_SLOT] == PCKBCCF_SLOT_DEFAULT)) + return (0); + + /* Flush any garbage. */ + pckbc_flush(pa->pa_tag, pa->pa_slot); + + /* Reset the keyboard. */ + cmd[0] = KBC_RESET; + res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1); + if (res) { +#ifdef DEBUG + printf("gsckbdprobe: reset error %d\n", res); +#endif + /* + * There is probably no keyboard connected. + * Let the probe succeed if the keyboard is used + * as console input - it can be connected later. + */ + return (gsckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0); + } + if (resp[0] != KBR_RSTDONE) { + printf("gsckbdprobe: reset response 0x%x\n", resp[0]); + return (0); + } + + /* + * Some keyboards seem to leave a second ack byte after the reset. + * This is kind of stupid, but we account for them anyway by just + * flushing the buffer. + */ + pckbc_flush(pa->pa_tag, pa->pa_slot); + + return (2); +} + +void +gsckbdattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct gsckbd_softc *sc = (void *)self; + struct pckbc_attach_args *pa = aux; + int isconsole; + struct wskbddev_attach_args a; + + printf("\n"); + + isconsole = gsckbd_is_console(pa->pa_tag, pa->pa_slot); + + if (isconsole) { + sc->id = &gsckbd_consdata; + sc->sc_enabled = 1; + } else { + u_char cmd[1]; + + sc->id = malloc(sizeof(struct gsckbd_internal), + M_DEVBUF, M_WAITOK); + (void) gsckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0); + + /* no interrupts until enabled */ + cmd[0] = KBC_DISABLE; + (void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, + cmd, 1, 0, 0, 0); + sc->sc_enabled = 0; + } + + sc->id->t_sc = sc; + + pckbc_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot, + gsckbd_input, sc, sc->sc_dev.dv_xname); + + a.console = isconsole; + + a.keymap = &gsckbd_keymapdata; + + a.accessops = &gsckbd_accessops; + a.accesscookie = sc; + + /* + * Attach the wskbd, saving a handle to it. + * XXX XXX XXX + */ + sc->sc_wskbddev = config_found(self, &a, wskbddevprint); +} + +int +gsckbd_enable(v, on) + void *v; + int on; +{ + struct gsckbd_softc *sc = v; + u_char cmd[1]; + int res; + + if (on) { + if (sc->sc_enabled) + return (EBUSY); + + pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1); + + cmd[0] = KBC_ENABLE; + res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, + cmd, 1, 0, 1, 0); + if (res) { + printf("gsckbd_enable: command error\n"); + return (res); + } + + sc->sc_enabled = 1; + } else { + if (sc->id->t_isconsole) + return (EBUSY); + + cmd[0] = KBC_DISABLE; + res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, + cmd, 1, 0, 1, 0); + if (res) { + printf("gsckbd_disable: command error\n"); + return (res); + } + + pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0); + + sc->sc_enabled = 0; + } + + return (0); +} + +static int +gsckbd_decode(id, datain, type, dataout) + struct gsckbd_internal *id; + int datain; + u_int *type; + int *dataout; +{ + int key; + + if (datain == KBR_BREAK) { + id->t_releasing = 1; /* next keycode is a release */ + return 0; + } + + if (datain == KBR_EXTENDED0) { + id->t_extended = 0x80; + return 0; + } else if (datain == KBR_EXTENDED1) { + id->t_extended1 = 2; + return 0; + } + + /* + * Map extended keys to codes 128-254 + * Note that we do not use (datain & 0x7f) because function key + * F7 produces non-extended 0x83 code. Sucker. + */ + key = datain | id->t_extended; + id->t_extended = 0; + + /* + * process BREAK sequence (EXT1 14 77): + * map to (unused) code 7F + */ + if (id->t_extended1 == 2 && datain == 0x14) { + id->t_extended1 = 1; + return 0; + } else if (id->t_extended1 == 1 && datain == 0x77) { + id->t_extended1 = 0; + key = 0x7f; + } else if (id->t_extended1 > 0) { + id->t_extended1 = 0; + } + + if (id->t_releasing) { + id->t_releasing = 0; + *type = WSCONS_EVENT_KEY_UP; + *dataout = key; + id->t_lastchar = 0; + } else { + /* Always ignore typematic keys */ + if (key == id->t_lastchar) + return 0; + *dataout = id->t_lastchar = key; + *type = WSCONS_EVENT_KEY_DOWN; + } + + return 1; +} + +int +gsckbd_init(t, kbctag, kbcslot, console) + struct gsckbd_internal *t; + pckbc_tag_t kbctag; + pckbc_slot_t kbcslot; + int console; +{ + bzero(t, sizeof(struct gsckbd_internal)); + + t->t_isconsole = console; + t->t_kbctag = kbctag; + t->t_kbcslot = kbcslot; + + return (0); +} + +static int +gsckbd_led_encode(led) + int led; +{ + int res; + + res = 0; + + if (led & WSKBD_LED_SCROLL) + res |= 0x01; + if (led & WSKBD_LED_NUM) + res |= 0x02; + if (led & WSKBD_LED_CAPS) + res |= 0x04; + return(res); +} + +static int +gsckbd_led_decode(led) + int led; +{ + int res; + + res = 0; + if (led & 0x01) + res |= WSKBD_LED_SCROLL; + if (led & 0x02) + res |= WSKBD_LED_NUM; + if (led & 0x04) + res |= WSKBD_LED_CAPS; + return(res); +} + +void +gsckbd_set_leds(v, leds) + void *v; + int leds; +{ + struct gsckbd_softc *sc = v; + u_char cmd[2]; + + cmd[0] = KBC_MODEIND; + cmd[1] = gsckbd_led_encode(leds); + sc->sc_ledstate = cmd[1]; + + pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, + cmd, 2, 0, 0, 0); +} + +/* + * Got a console receive interrupt - + * the console processor wants to give us a character. + */ +void +gsckbd_input(vsc, data) + void *vsc; + int data; +{ + struct gsckbd_softc *sc = vsc; + int type, key; + +#ifdef WSDISPLAY_COMPAT_RAWKBD + if (sc->rawkbd) { + char d = data; + wskbd_rawinput(sc->sc_wskbddev, &d, 1); + return; + } +#endif + if (gsckbd_decode(sc->id, data, &type, &key)) + wskbd_input(sc->sc_wskbddev, type, key); +} + +int +gsckbd_ioctl(v, cmd, data, flag, p) + void *v; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct gsckbd_softc *sc = v; + + switch (cmd) { + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_PC_XT; + return 0; + case WSKBDIO_SETLEDS: + { + char cmd[2]; + int res; + cmd[0] = KBC_MODEIND; + cmd[1] = gsckbd_led_encode(*(int *)data); + sc->sc_ledstate = cmd[1]; + res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, + cmd, 2, 0, 1, 0); + return (res); + } + case WSKBDIO_GETLEDS: + *(int *)data = gsckbd_led_decode(sc->sc_ledstate); + return (0); +#ifdef WSDISPLAY_COMPAT_RAWKBD + case WSKBDIO_SETMODE: + sc->rawkbd = (*(int *)data == WSKBD_RAW); + return (0); +#endif + } + return -1; +} + +int +gsckbd_cnattach(kbctag, kbcslot) + pckbc_tag_t kbctag; + int kbcslot; +{ + char cmd[1]; + int res; + + res = gsckbd_init(&gsckbd_consdata, kbctag, kbcslot, 1); +#if 0 /* we allow the console to be attached if no keyboard is present */ + if (res) + return (res); +#endif + + /* Just to be sure. */ + cmd[0] = KBC_ENABLE; + res = pckbc_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0); +#if 0 + if (res) + return (res); +#endif + + wskbd_cnattach(&gsckbd_consops, &gsckbd_consdata, &gsckbd_keymapdata); + + return (0); +} + +/* ARGSUSED */ +void +gsckbd_cngetc(v, type, data) + void *v; + u_int *type; + int *data; +{ + struct gsckbd_internal *t = v; + int val; + + for (;;) { + val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot); + if ((val != -1) && gsckbd_decode(t, val, type, data)) + return; + } +} + +void +gsckbd_cnpollc(v, on) + void *v; + int on; +{ + struct gsckbd_internal *t = v; + + pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on); +} diff --git a/sys/arch/hppa/gsc/gsckbdmap.c b/sys/arch/hppa/gsc/gsckbdmap.c new file mode 100644 index 00000000000..688e25bd853 --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbdmap.c @@ -0,0 +1,169 @@ +/* $OpenBSD: gsckbdmap.c,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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. + * + */ + +#include <sys/types.h> +#include <dev/wscons/wsksymdef.h> +#include <dev/wscons/wsksymvar.h> +#include <hppa/gsc/gsckbdmap.h> + +#define KC(n) KS_KEYCODE(n) + +static const keysym_t gsckbd_keydesc_us[] = { +/* pos command normal shifted */ + KC(1), KS_Cmd_Screen8, KS_f9, + KC(3), KS_Cmd_Screen4, KS_f5, + KC(4), KS_Cmd_Screen2, KS_f3, + KC(5), KS_Cmd_Screen0, KS_f1, + KC(6), KS_Cmd_Screen1, KS_f2, + KC(7), KS_Cmd_Screen11, KS_f12, + KC(9), KS_Cmd_Screen9, KS_f10, + KC(10), KS_Cmd_Screen7, KS_f8, + KC(11), KS_Cmd_Screen5, KS_f6, + KC(12), KS_Cmd_Screen3, KS_f4, + KC(13), KS_Tab, + KC(14), KS_grave, KS_asciitilde, + KC(17), KS_Cmd2, KS_Alt_L, + KC(18), KS_Shift_L, + KC(20), KS_Cmd1, KS_Control_L, + KC(21), KS_q, + KC(22), KS_1, KS_exclam, + KC(26), KS_z, + KC(27), KS_s, + KC(28), KS_a, + KC(29), KS_w, + KC(30), KS_2, KS_at, + KC(33), KS_c, + KC(34), KS_x, + KC(35), KS_d, + KC(36), KS_e, + KC(37), KS_4, KS_dollar, + KC(38), KS_3, KS_numbersign, + KC(41), KS_space, + KC(42), KS_v, + KC(43), KS_f, + KC(44), KS_t, + KC(45), KS_r, + KC(46), KS_5, KS_percent, + KC(49), KS_n, + KC(50), KS_b, + KC(51), KS_h, + KC(52), KS_g, + KC(53), KS_y, + KC(54), KS_6, KS_asciicircum, + KC(58), KS_m, + KC(59), KS_j, + KC(60), KS_u, + KC(61), KS_7, KS_ampersand, + KC(62), KS_8, KS_asterisk, + KC(65), KS_comma, KS_less, + KC(66), KS_k, + KC(67), KS_i, + KC(68), KS_o, + KC(69), KS_0, KS_parenright, + KC(70), KS_9, KS_parenleft, + KC(73), KS_period, KS_greater, + KC(74), KS_slash, KS_question, + KC(75), KS_l, + KC(76), KS_semicolon, KS_colon, + KC(77), KS_p, + KC(78), KS_minus, KS_underscore, + KC(82), KS_apostrophe, KS_quotedbl, + KC(84), KS_bracketleft, KS_braceleft, + KC(85), KS_equal, KS_plus, + KC(88), KS_Caps_Lock, + KC(89), KS_Shift_R, + KC(90), KS_Return, + KC(91), KS_bracketright,KS_braceright, + KC(93), KS_backslash, KS_bar, + KC(102), KS_Cmd_ResetEmul, KS_Delete, /* Backspace */ + KC(105), KS_KP_End, KS_KP_1, + KC(107), KS_KP_Left, KS_KP_4, + KC(108), KS_KP_Home, KS_KP_7, + KC(112), KS_KP_Insert, KS_KP_0, + KC(113), KS_Cmd_KbdReset, KS_KP_Delete, KS_KP_Decimal, + KC(114), KS_KP_Down, KS_KP_2, + KC(115), KS_KP_Begin, KS_KP_5, + KC(116), KS_KP_Right, KS_KP_6, + KC(117), KS_KP_Up, KS_KP_8, + KC(118), KS_Cmd_Debugger, KS_Escape, + KC(119), KS_Num_Lock, + KC(120), KS_Cmd_Screen10, KS_f11, + KC(121), KS_KP_Add, + KC(122), KS_KP_Next, KS_KP_3, + KC(123), KS_KP_Subtract, + KC(124), KS_KP_Multiply, + KC(125), KS_KP_Prior, KS_KP_9, + KC(126), KS_Hold_Screen, + KC(127), KS_Pause, /* Break */ + KC(131), KS_Cmd_Screen6, KS_f7, + KC(145), KS_Cmd2, KS_Alt_R, KS_Multi_key, + KC(148), KS_Cmd1, KS_Control_R, + KC(202), KS_KP_Divide, + KC(218), KS_KP_Enter, + KC(233), KS_End, + KC(235), KS_Left, + KC(236), KS_Home, + KC(240), KS_Insert, + KC(241), KS_Delete, + KC(242), KS_Down, + KC(244), KS_Right, + KC(245), KS_Up, + KC(250), KS_Cmd_ScrollFwd, KS_Next, + /* + * Print Screen produces E0 12 E0 7C when pressed, then E0 7C E0 12 when + * depressed. Ignore the E0 12 code and recognize on E0 7C. + */ + KC(252), KS_Print_Screen, + KC(253), KS_Cmd_ScrollBack, KS_Prior, + KC(254), KS_Cmd_ResetClose, /* CTL-Break */ +}; + +static const keysym_t gsckbd_keydesc_swapctrlcaps[] = { +/* pos command normal shifted */ + KC(20), KS_Caps_Lock, + KC(88), KS_Cmd1, KS_Control_L, +}; + +static const keysym_t gsckbd_keydesc_uk[] = { + KC(14), KS_grave, KS_notsign, + KC(30), KS_2, KS_quotedbl, + KC(38), KS_3, KS_sterling, + KC(82), KS_apostrophe, KS_at, + KC(93), KS_numbersign, KS_asciitilde, + KC(97), KS_backslash, KS_bar, +}; + +#define KBD_MAP(name, base, map) \ + { name, base, sizeof(map)/sizeof(keysym_t), map } + +const struct wscons_keydesc gsckbd_keydesctab[] = { + KBD_MAP(KB_US, 0, gsckbd_keydesc_us), + KBD_MAP(KB_UK, KB_US, gsckbd_keydesc_uk), + KBD_MAP(KB_US | KB_SWAPCTRLCAPS, KB_US, gsckbd_keydesc_swapctrlcaps), + {0, 0, 0, 0} +}; diff --git a/sys/arch/hppa/gsc/gsckbdmap.h b/sys/arch/hppa/gsc/gsckbdmap.h new file mode 100644 index 00000000000..83ec0b9119f --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbdmap.h @@ -0,0 +1,29 @@ +/* $OpenBSD: gsckbdmap.h,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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. + * + */ + +extern const struct wscons_keydesc gsckbd_keydesctab[]; diff --git a/sys/arch/hppa/gsc/gsckbdvar.h b/sys/arch/hppa/gsc/gsckbdvar.h new file mode 100644 index 00000000000..6e72a0dc16c --- /dev/null +++ b/sys/arch/hppa/gsc/gsckbdvar.h @@ -0,0 +1,29 @@ +/* $OpenBSD: gsckbdvar.h,v 1.1 2003/01/31 22:50:19 miod Exp $ */ +/* + * Copyright (c) 2003, Miodrag Vallat. + * 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. + * + * 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. + * + */ + +int gsckbd_cnattach(pckbc_tag_t, int); |