/* $Id: jmcu.c,v 1.4 2008/09/30 19:35:06 nbrk Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wskbd.h" #define JMCU_KBD_GPIOINTR 0 #define JMCU_TS_GPIOINTR 15 #define JMCU_KBD_NEVENTS 8 struct jmcu_softc { struct device sc_dev; /* parent SPI stuff */ struct spi_bus *sc_bus; void *sc_spisc; /* keyboard stuff */ struct device *sc_wskbddev; int sc_polling; /* if in 'poll' mode now */ u_int sc_kbdqtypes[JMCU_KBD_NEVENTS]; uint8_t sc_kbdqkeys[JMCU_KBD_NEVENTS]; uint8_t sc_kbdnevents; /* battery charge */ uint16_t sc_batmain; uint16_t sc_batback; }; struct jmcu_softc *jmcu_sc; struct wskbd_mapdata jmcukbd_keymapdata = { jmcukbd_keydesctab, KB_US, }; /* * Prototypes. */ int jmcu_match(struct device *parent, void *cf, void *aux); void jmcu_attach(struct device *parent, struct device *self, void *aux); int jmcu_kbdintr(void *arg); int jmcu_touchintr(void *arg); void jmcukbd_cngetc(void *v, u_int *type, int *data); void jmcukbd_cnpollc(void *v, int on); void jmcukbd_poll(void *v); void jmcukbd_enqueue(struct jmcu_softc *sc, u_int type, uint8_t key); void jmcukbd_dequeue(struct jmcu_softc *sc, u_int *type, int *data); void jmcubatt_getstatus(struct jmcu_softc *sc); void jmcukbd_readcodes(struct jmcu_softc *sc, char *buf); /* accessops */ int jmcukbd_enable(void *, int); void jmcukbd_set_leds(void *, int); int jmcukbd_ioctl(void *, u_long, caddr_t, int, struct proc *); /* * Autoconf glue. */ struct cfattach jmcu_ca = { sizeof(struct jmcu_softc), jmcu_match, jmcu_attach, NULL, NULL }; struct cfdriver jmcu_cd = { NULL, "jmcu", DV_DULL }; /* * Console ops. */ struct wskbd_consops jmcukbd_consops = { jmcukbd_cngetc, jmcukbd_cnpollc, NULL /* bell */ }; struct wskbd_accessops jmcukbd_accessops = { jmcukbd_enable, jmcukbd_set_leds, jmcukbd_ioctl, }; int jmcu_match(struct device *parent, void *cf, void *aux) { return (1); } void jmcu_attach(struct device *parent, struct device *self, void *aux) { struct jmcu_softc *sc = (struct jmcu_softc *)self; struct spibus_attach_args *sba = aux; #if NWSKBD > 0 struct wskbddev_attach_args wda; #endif /* leap to parent */ sc->sc_bus = sba->sba_bus; sc->sc_spisc = sba->sba_spisc; jmcu_sc = sc; /* jmcubatt_getstatus(sc); */ printf(": Jornada MCU (battery charge: main=%d backup=%d)\n", sc->sc_batmain, sc->sc_batback); #if 0 sa11x0_gpio_intr_establish(JMCU_KBD_GPIOINTR, IST_EDGE_FALLING, IPL_TTY, jmcu_kbdintr, sc, "jmcukbd_intr"); #endif /* 0 */ #if NWSKBD > 0 /* empty event queue */ sc->sc_kbdnevents = 0; wskbd_cnattach(&jmcukbd_consops, sc, &jmcukbd_keymapdata); wda.console = 1; wda.keymap = &jmcukbd_keymapdata; wda.accessops = &jmcukbd_accessops; wda.accesscookie = sc; sc->sc_wskbddev = config_found(self, &wda, wskbddevprint); #endif return; } #if 0 int jmcu_kbdintr(void *arg) { jmcukbd_poll(arg); return(0); } #endif #if 0 int jmcu_touchintr(void *arg) { struct jmcu_softc *sc = arg; printf("%s: jmcu_touchintr\n", sc->sc_dev.dv_xname); return(0); } #endif void jmcukbd_cngetc(void *v, u_int *type, int *data) { struct jmcu_softc *sc = v; /* XXX we should block until new keycode has been obtained */ sc->sc_polling = 1; while (sc->sc_kbdnevents == 0) { /* enqueue events */ jmcukbd_poll(v); } sc->sc_polling = 0; jmcukbd_dequeue(sc, type, data); } void jmcukbd_cnpollc(void *v, int on) { } void jmcukbd_poll(void *v) { struct jmcu_softc *sc = v; u_int type; int key, s; char buf[JMCU_KBD_NEVENTS + 1], *p; s = spltty(); jmcukbd_readcodes(sc, buf); for (p = buf; *p; p++) { type = *p & 0x80 ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; key = *p & 0x7f; /* XXX */ if (sc->sc_polling == 0) wskbd_input(sc->sc_wskbddev, type, key); else { /* enqueue event */ jmcukbd_enqueue(sc, type, key); } } splx(s); } void jmcukbd_readcodes(struct jmcu_softc *sc, char *buf) { int nkeys; /* wake up the MCU */ sa11x0_gpio_clear_bit(25); spi_acquire(sc->sc_spisc); /* ask mcu about how many keypress events are there; should get TXDUMMY here */ if (spi_shift_1(sc->sc_spisc, JMCU_CMD_GETKEYCODES) != JMCU_CMD_TXDUMMY) { printf("%s: WARNING, no txdummy received\n", sc->sc_dev.dv_xname); goto out; } nkeys = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY); while (nkeys) { *buf = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY); nkeys--; buf++; } out: /* terminate buffer */ *buf = 0; spi_release(sc->sc_spisc); /* park off the MCU */ sa11x0_gpio_set_bit(25); } void jmcukbd_enqueue(struct jmcu_softc *sc, u_int type, uint8_t key) { if (sc->sc_kbdnevents > JMCU_KBD_NEVENTS) { printf("%s: WARNING, event queue is full; keycode 0x%x will be discarded\n", sc->sc_dev.dv_xname, key); return; } sc->sc_kbdqtypes[sc->sc_kbdnevents] = type; sc->sc_kbdqkeys[sc->sc_kbdnevents] = key; sc->sc_kbdnevents++; } void jmcukbd_dequeue(struct jmcu_softc *sc, u_int *type, int *data) { #if 0 if (sc->sc_kbdeventsnum < 1) { printf("%s: WARNING, event queue is empty\n", sc->sc_dev.dv_xname); return; } #endif sc->sc_kbdnevents--; *type = sc->sc_kbdqtypes[sc->sc_kbdnevents]; *data = sc->sc_kbdqkeys[sc->sc_kbdnevents]; } void jmcubatt_getstatus(struct jmcu_softc *sc) { uint8_t fraction; /* wake up the MCU */ sa11x0_gpio_clear_bit(25); spi_acquire(sc->sc_spisc); spi_shift_1(sc->sc_spisc, JMCU_CMD_GETBATTERYDATA); sc->sc_batmain = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY); sc->sc_batback = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY); fraction = spi_shift_1(sc->sc_spisc, JMCU_CMD_TXDUMMY); spi_release(sc->sc_spisc); /* park off the MCU */ sa11x0_gpio_set_bit(25); /* fraction[0:1] is mainbatt[8:9]; fraction[2:3] is backbatt[8:9] */ sc->sc_batmain |= (uint16_t)((fraction & 0x03) << 8); sc->sc_batback |= (uint16_t)((fraction & 0x0c) << 6); } int jmcukbd_enable(void *v, int on) { return 0; } void jmcukbd_set_leds(void *v, int on) { } int jmcukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) { #ifdef WSDISPLAY_COMPAT_RAWKBD struct jmcukbd_softc *sc = v; #endif switch (cmd) { case WSKBDIO_GTYPE: *(int *)data = WSKBD_TYPE_ZAURUS /* XXX WSKBD_TYPE_JORNADA */; return 0; case WSKBDIO_SETLEDS: return 0; case WSKBDIO_GETLEDS: *(int *)data = 0; return 0; #ifdef WSDISPLAY_COMPAT_RAWKBD case WSKBDIO_SETMODE: sc->sc_rawkbd = *(int *)data == WSKBD_RAW; timeout_del(&sc->sc_rawrepeat_ch); return (0); #endif } /* kbdioctl(...); */ return -1; }