/* $OpenBSD: comkbd_ebus.c,v 1.21 2011/03/18 21:01:17 miod Exp $ */ /* * Copyright (c) 2002 Jason L. Wright (jason@thought.net) * 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. * * Effort sponsored in part by the Defense Advanced Research Projects * Agency (DARPA) and Air Force Research Laboratory, Air Force * Materiel Command, USAF, under agreement number F30602-01-2-0537. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define COMK_RX_RING 64 #define COMK_TX_RING 64 struct comkbd_softc { struct sunkbd_softc sc_base; bus_space_tag_t sc_iot; /* bus tag */ bus_space_handle_t sc_ioh; /* bus handle */ void *sc_ih, *sc_si; /* interrupt vectors */ u_int sc_rxcnt; u_int8_t sc_rxbuf[COMK_RX_RING]; u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput; u_int sc_txcnt; u_int8_t sc_txbuf[COMK_TX_RING]; u_int8_t *sc_txbeg, *sc_txend, *sc_txget, *sc_txput; u_int8_t sc_ier; }; #define COM_WRITE(sc,r,v) \ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v)) #define COM_READ(sc,r) \ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r)) int comkbd_match(struct device *, void *, void *); void comkbd_attach(struct device *, struct device *, void *); int comkbd_iskbd(int); /* wskbd glue */ void comkbd_cnpollc(void *, int); void comkbd_cngetc(void *, u_int *, int *); /* internals */ int comkbd_enqueue(void *, u_int8_t *, u_int); int comkbd_init(struct comkbd_softc *); void comkbd_putc(struct comkbd_softc *, u_int8_t); int comkbd_intr(void *); void comkbd_soft(void *); struct cfattach comkbd_ca = { sizeof(struct comkbd_softc), comkbd_match, comkbd_attach }; struct cfdriver comkbd_cd = { NULL, "comkbd", DV_DULL }; const char *comkbd_names[] = { "su", "su_pnp", NULL }; struct wskbd_consops comkbd_consops = { comkbd_cngetc, comkbd_cnpollc }; int comkbd_iskbd(node) int node; { if (OF_getproplen(node, "keyboard") == 0) return (10); return (0); } int comkbd_match(parent, match, aux) struct device *parent; void *match; void *aux; { struct ebus_attach_args *ea = aux; int i; for (i = 0; comkbd_names[i]; i++) if (strcmp(ea->ea_name, comkbd_names[i]) == 0) return (comkbd_iskbd(ea->ea_node)); if (strcmp(ea->ea_name, "serial") == 0) { char compat[80]; if ((i = OF_getproplen(ea->ea_node, "compatible")) && OF_getprop(ea->ea_node, "compatible", compat, sizeof(compat)) == i) { if (strcmp(compat, "su16550") == 0 || strcmp(compat, "su") == 0) return (comkbd_iskbd(ea->ea_node)); } } return (0); } void comkbd_attach(parent, self, aux) struct device *parent, *self; void *aux; { struct comkbd_softc *sc = (void *)self; struct sunkbd_softc *ss = (void *)sc; struct ebus_attach_args *ea = aux; struct wskbddev_attach_args a; int console; ss->sc_sendcmd = comkbd_enqueue; timeout_set(&ss->sc_bellto, sunkbd_bellstop, sc); sc->sc_iot = ea->ea_memtag; sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf; sc->sc_rxend = sc->sc_rxbuf + COMK_RX_RING; sc->sc_rxcnt = 0; sc->sc_txget = sc->sc_txput = sc->sc_txbeg = sc->sc_txbuf; sc->sc_txend = sc->sc_txbuf + COMK_TX_RING; sc->sc_txcnt = 0; console = (ea->ea_node == OF_instance_to_package(OF_stdin())); sc->sc_si = softintr_establish(IPL_TTY, comkbd_soft, sc); if (sc->sc_si == NULL) { printf(": can't get soft intr\n"); return; } /* Use prom address if available, otherwise map it. */ if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0, BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_memtag; } else if (ebus_bus_map(ea->ea_memtag, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_memtag; } else if (ebus_bus_map(ea->ea_iotag, 0, EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) { sc->sc_iot = ea->ea_iotag; } else { printf(": can't map register space\n"); return; } sc->sc_ih = bus_intr_establish(sc->sc_iot, ea->ea_intrs[0], IPL_TTY, 0, comkbd_intr, sc, self->dv_xname); if (sc->sc_ih == NULL) { printf(": can't get hard intr\n"); return; } if (comkbd_init(sc) == 0) { return; } ss->sc_click = strcmp(getpropstring(optionsnode, "keyboard-click?"), "true") == 0; sunkbd_setclick(ss, ss->sc_click); a.console = console; if (ISTYPE5(ss->sc_layout)) { a.keymap = &sunkbd5_keymapdata; #ifndef SUNKBD5_LAYOUT if (ss->sc_layout < MAXSUNLAYOUT && sunkbd_layouts[ss->sc_layout] != -1) sunkbd5_keymapdata.layout = sunkbd_layouts[ss->sc_layout]; #endif } else { a.keymap = &sunkbd_keymapdata; #ifndef SUNKBD_LAYOUT if (ss->sc_layout < MAXSUNLAYOUT && sunkbd_layouts[ss->sc_layout] != -1) sunkbd_keymapdata.layout = sunkbd_layouts[ss->sc_layout]; #endif } a.accessops = &sunkbd_accessops; a.accesscookie = sc; if (console) { cn_tab->cn_dev = makedev(77, ss->sc_dev.dv_unit); /* XXX */ cn_tab->cn_pollc = wskbd_cnpollc; cn_tab->cn_getc = wskbd_cngetc; wskbd_cnattach(&comkbd_consops, sc, a.keymap); sc->sc_ier = IER_ETXRDY | IER_ERXRDY; COM_WRITE(sc, com_ier, sc->sc_ier); COM_READ(sc, com_iir); COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS); } sunkbd_attach(ss, &a); } void comkbd_cnpollc(vsc, on) void *vsc; int on; { } void comkbd_cngetc(v, type, data) void *v; u_int *type; int *data; { struct comkbd_softc *sc = v; int s; u_int8_t c; s = splhigh(); while (1) { if (COM_READ(sc, com_lsr) & LSR_RXRDY) break; } c = COM_READ(sc, com_data); COM_READ(sc, com_iir); splx(s); sunkbd_decode(c, type, data); } void comkbd_putc(sc, c) struct comkbd_softc *sc; u_int8_t c; { int s, timo; s = splhigh(); timo = 150000; while (--timo) { if (COM_READ(sc, com_lsr) & LSR_TXRDY) break; } COM_WRITE(sc, com_data, c); bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, COM_NPORTS, BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); timo = 150000; while (--timo) { if (COM_READ(sc, com_lsr) & LSR_TXRDY) break; } splx(s); } int comkbd_enqueue(v, buf, buflen) void *v; u_int8_t *buf; u_int buflen; { struct comkbd_softc *sc = v; int s; u_int i; s = spltty(); /* See if there is room... */ if ((sc->sc_txcnt + buflen) > COMK_TX_RING) { splx(s); return (-1); } for (i = 0; i < buflen; i++) { *sc->sc_txget = *buf; buf++; sc->sc_txcnt++; sc->sc_txget++; if (sc->sc_txget == sc->sc_txend) sc->sc_txget = sc->sc_txbeg; } comkbd_soft(sc); splx(s); return (0); } void comkbd_soft(vsc) void *vsc; { struct comkbd_softc *sc = vsc; struct sunkbd_softc *ss = (void *)sc; u_int8_t cbuf[SUNKBD_MAX_INPUT_SIZE], *cptr; u_int8_t c; cptr = cbuf; while (sc->sc_rxcnt) { *cptr++ = *sc->sc_rxget; if (++sc->sc_rxget == sc->sc_rxend) sc->sc_rxget = sc->sc_rxbeg; sc->sc_rxcnt--; if (cptr - cbuf == sizeof cbuf) { sunkbd_input(ss, cbuf, cptr - cbuf); cptr = cbuf; } } if (cptr != cbuf) sunkbd_input(ss, cbuf, cptr - cbuf); if (sc->sc_txcnt) { c = sc->sc_ier | IER_ETXRDY; if (c != sc->sc_ier) { COM_WRITE(sc, com_ier, c); sc->sc_ier = c; } if (COM_READ(sc, com_lsr) & LSR_TXRDY) { sc->sc_txcnt--; COM_WRITE(sc, com_data, *sc->sc_txput); if (++sc->sc_txput == sc->sc_txend) sc->sc_txput = sc->sc_txbeg; } } } int comkbd_intr(vsc) void *vsc; { struct comkbd_softc *sc = vsc; u_int8_t iir, lsr, data; int needsoft = 0; /* Nothing to do */ iir = COM_READ(sc, com_iir); if (iir & IIR_NOPEND) return (0); for (;;) { lsr = COM_READ(sc, com_lsr); if (lsr & LSR_RXRDY) { needsoft = 1; do { data = COM_READ(sc, com_data); if (sc->sc_rxcnt != COMK_RX_RING) { *sc->sc_rxput = data; if (++sc->sc_rxput == sc->sc_rxend) sc->sc_rxput = sc->sc_rxbeg; sc->sc_rxcnt++; } lsr = COM_READ(sc, com_lsr); } while (lsr & LSR_RXRDY); } if (lsr & LSR_TXRDY) { if (sc->sc_txcnt == 0) { /* Nothing further to send */ sc->sc_ier &= ~IER_ETXRDY; COM_WRITE(sc, com_ier, sc->sc_ier); } else needsoft = 1; } iir = COM_READ(sc, com_iir); if (iir & IIR_NOPEND) break; } if (needsoft) softintr_schedule(sc->sc_si); return (1); } int comkbd_init(sc) struct comkbd_softc *sc; { struct sunkbd_softc *ss = (void *)sc; u_int8_t stat, c; int tries; for (tries = 5; tries != 0; tries--) { int ltries; ss->sc_leds = 0; ss->sc_layout = -1; /* Send reset request */ comkbd_putc(sc, SKBD_CMD_RESET); ltries = 1000; while (--ltries > 0) { stat = COM_READ(sc,com_lsr); if (stat & LSR_RXRDY) { c = COM_READ(sc, com_data); sunkbd_raw(ss, c); if (ss->sc_kbdstate == SKBD_STATE_RESET) break; } DELAY(1000); } if (ltries == 0) continue; /* Wait for reset to finish. */ ltries = 1000; while (--ltries > 0) { stat = COM_READ(sc, com_lsr); if (stat & LSR_RXRDY) { c = COM_READ(sc, com_data); sunkbd_raw(ss, c); if (ss->sc_kbdstate == SKBD_STATE_GETKEY) break; } DELAY(1000); } if (ltries == 0) continue; /* Some Sun<=>PS/2 converters need some delay here */ DELAY(5000); /* Send layout request */ comkbd_putc(sc, SKBD_CMD_LAYOUT); ltries = 1000; while (--ltries > 0) { stat = COM_READ(sc, com_lsr); if (stat & LSR_RXRDY) { c = COM_READ(sc, com_data); sunkbd_raw(ss, c); if (ss->sc_layout != -1) break; } DELAY(1000); } if (ltries != 0) break; } if (tries == 0) printf(": no keyboard\n"); else printf(": layout %d\n", ss->sc_layout); return tries; }