From ccd1cf10236b807588e347f76a787e9302f67295 Mon Sep 17 00:00:00 2001 From: joff Date: Mon, 31 Jan 2005 06:03:40 +0000 Subject: [PATCH] Matrix keypad wskbd(4) support. Matrix keypads are cheap/rugged user input devices found in many embedded systems. They must be polled and debounced in software. Should be able to handle any size matrix keypad, but only tested with a 4x4 (16-key) device attached to the TS-7200 ARM embedded board via the DIO header. --- sys/dev/ic/matrixkp_subr.c | 212 +++++++++++++++++++++++++++++++++++++ sys/dev/ic/matrixkpvar.h | 58 ++++++++++ 2 files changed, 270 insertions(+) create mode 100644 sys/dev/ic/matrixkp_subr.c create mode 100644 sys/dev/ic/matrixkpvar.h diff --git a/sys/dev/ic/matrixkp_subr.c b/sys/dev/ic/matrixkp_subr.c new file mode 100644 index 000000000000..0e8974925107 --- /dev/null +++ b/sys/dev/ic/matrixkp_subr.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2005 Jesse Off. 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 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 AUTHOR 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. + * + * + * The matrix keypad is a primitive type of keying device + * commonly used in systems as a small, cheap, easy-to-build and rugged + * way to get user input in a variety of embedded environments. This + * driver can work for any size of keypad. A one key keypad (aka + * button) can also be used. The theory of operation is described + * thusly: + * + * 1) The keypad is connected to the NetBSD embedded system + * with digital I/O (DIO) pins connected to each column of + * the keypad and also to each row of the keypad. + * + * 2) When a button is pressed, a short is made between a + * column line and the intersecting row line. + * + * 3) Software is responsible to poll each row/column individually + * and also to debounce any key presses. + * + * To correctly wire up such a thing requires the input DIO + * lines to have pull-up resistors, otherwise an input may be read as a random + * value if not currently being shorted by a button press. + */ + +#include +__KERNEL_RCSID(0, "$NetBSD: matrixkp_subr.c,v 1.1 2005/01/31 06:03:40 joff Exp $"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define TV_ELAPSED_US(x, y) (((x).tv_sec - (y).tv_sec) * 1000000 + \ + ((x).tv_usec - (y).tv_usec)) + +const struct wskbd_accessops mxkp_accessops = { + mxkp_enable, + mxkp_set_leds, + mxkp_ioctl, +}; + +void +mxkp_attach(struct matrixkp_softc *sc) +{ + u_int32_t i; + + callout_init(&sc->sc_callout); + callout_setfunc(&sc->sc_callout, mxkp_poll, sc); + if (sc->poll_freq > hz || sc->poll_freq == 0) + sc->poll_freq = hz; + sc->sc_enabled = 0; + if (sc->debounce_stable_ms == 0) + sc->sc_flags |= MXKP_NODEBOUNCE; + if (sc->mxkp_event == NULL) + sc->mxkp_event = mxkp_wskbd_event; + FOR_KEYS(i, sc->mxkp_pressed[i] = 0); +} + +void +mxkp_poll(void *arg) +{ + struct matrixkp_softc *sc = (struct matrixkp_softc *)arg; + u_int32_t i, anychanged; + u_int32_t scanned[(MAXNKEYS + 31) / 32]; + u_int32_t changed[(MAXNKEYS + 31) / 32]; + u_int32_t set[(MAXNKEYS + 31) / 32]; + u_int32_t cleared[(MAXNKEYS + 31) / 32]; + +rescan: + anychanged = 0; + FOR_KEYS(i, scanned[i] = 0); + sc->mxkp_scankeys(sc, scanned); + FOR_KEYS(i, changed[i] = sc->mxkp_pressed[i] ^ scanned[i]); + FOR_KEYS(i, anychanged |= changed[i]); + + if (!(sc->sc_flags & MXKP_NODEBOUNCE) && anychanged) { + mxkp_debounce(sc, changed, scanned); + anychanged = 0; + FOR_KEYS(i, changed[i] &= sc->mxkp_pressed[i] ^ scanned[i]); + FOR_KEYS(i, anychanged |= changed[i]); + } + if (anychanged) { + FOR_KEYS(i, set[i] = changed[i] & scanned[i]); + FOR_KEYS(i, cleared[i] = changed[i] & sc->mxkp_pressed[i]); + sc->mxkp_event(sc, set, cleared); + FOR_KEYS(i, sc->mxkp_pressed[i] &= ~cleared[i]); + FOR_KEYS(i, sc->mxkp_pressed[i] |= set[i]); + goto rescan; + } + if (sc->sc_enabled) + callout_schedule(&sc->sc_callout, hz / sc->poll_freq); +} + +/* + * debounce will return when masked keys have been stable + * for sc->debounce_stable_ms + */ +void +mxkp_debounce(struct matrixkp_softc *sc, u_int32_t *mask, u_int32_t *scan) { + struct timeval verystart, start, now; + u_int32_t last_val[(MAXNKEYS + 31) / 32]; + u_int32_t anyset, i; + + FOR_KEYS(i, last_val[i] = scan[i]); + microtime(&verystart); + start = verystart; + do { + FOR_KEYS(i, scan[i] = 0); + sc->mxkp_scankeys(sc, scan); + microtime(&now); + anyset = 0; + FOR_KEYS(i, anyset |= (scan[i] ^ last_val[i]) & mask[i]); + if (anyset) /* bounce detected */ + start = now; + FOR_KEYS(i, last_val[i] = scan[i]); + } while (TV_ELAPSED_US(now, start) <= (sc->debounce_stable_ms * 1000)); +} + +void +mxkp_wskbd_event(struct matrixkp_softc *sc, u_int32_t *on, u_int32_t *off) +{ + unsigned int i; + + for(i = 0; i < sc->mxkp_nkeys; i++) { + if (off[i / 32] & (1 << (i % 32))) { + wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, i); + } + } + for(i = 0; i < sc->mxkp_nkeys; i++) { + if (on[i / 32] & (1 << (i % 32))) { + wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, i); + } + } +} + +int +mxkp_enable(void *v, int on) +{ + struct matrixkp_softc *sc = v; + + if (on) { + if (sc->sc_enabled) + return EBUSY; + + sc->sc_enabled = 1; + callout_schedule(&sc->sc_callout, hz / sc->poll_freq); + } else { + sc->sc_enabled = 0; + } + + return 0; +} + +void +mxkp_set_leds(void *v, int leds) +{ +} + +int +mxkp_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + switch (cmd) { + case WSKBDIO_GTYPE: + *(int *)data = WSKBD_TYPE_MATRIXKP; + return 0; + case WSKBDIO_SETLEDS: + return 0; + case WSKBDIO_GETLEDS: + *(int *)data = 0; + return 0; + case WSKBDIO_COMPLEXBELL: + return 0; + } + return EPASSTHROUGH; +} diff --git a/sys/dev/ic/matrixkpvar.h b/sys/dev/ic/matrixkpvar.h new file mode 100644 index 000000000000..d0448b1cc427 --- /dev/null +++ b/sys/dev/ic/matrixkpvar.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2005 Jesse Off. 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 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 AUTHOR 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. + */ + +#ifndef _DEV_IC_MATRIXKPVAR_H_ +#define _DEV_IC_MATRIXKPVAR_H_ + +#define MAXNKEYS 64 +#define FOR_KEYS(x, y) \ + for((x) = 0; (x) < ((MAXNKEYS + 31) / 32); (x)++) y + +struct matrixkp_softc { + int mxkp_nkeys; + u_int32_t mxkp_pressed[(MAXNKEYS + 31) / 32]; + int poll_freq; + u_int32_t debounce_stable_ms; + void (*mxkp_scankeys)(struct matrixkp_softc *, u_int32_t *); + void (*mxkp_event)(struct matrixkp_softc *, u_int32_t *, u_int32_t *); +#define MXKP_NODEBOUNCE 0x1 + u_int32_t sc_flags; + u_int32_t sc_enabled; + struct device *sc_dev; + struct device *sc_wskbddev; + struct callout sc_callout; +}; + +void mxkp_attach(struct matrixkp_softc *); +void mxkp_poll(void *); +void mxkp_debounce(struct matrixkp_softc *, u_int32_t *, u_int32_t *); +void mxkp_wskbd_event(struct matrixkp_softc *, u_int32_t *, u_int32_t *); +int mxkp_enable(void *, int); +void mxkp_set_leds(void *, int); +int mxkp_ioctl(void *, u_long, caddr_t, int, struct proc *); + +const struct wskbd_accessops mxkp_accessops; + +#endif