Module Name: src Committed By: tsutsui Date: Sat Sep 18 13:44:02 UTC 2021
Modified Files: src/sys/arch/luna68k/dev: lunaws.c Log Message: Implement transmitting keyboard LED and buzzer control commands. - enable TX on uPD7201 for keyboard port - prepare TX queue and handle it in hardware interrupt and softint(9) - send proper LED commands on WSKBDIO_SETLEDS (XXX: KANA LED is not handled in wscons) - return current LED settings on WSKBDIO_GETLEDS - implement WSKBDIO_COMPLEXBELL by parsing struct wskbd_bell_data and send proper buzzer commands - handle pitch, period, and volume in cnbell(9) (XXX: no description in cnbell(9) man pages) - use proper queued TX function for omms_enable() and omms_disable() - add DPRINTF()s for debug - use C99 designated initializer and misc cosmetics Tested on LUNA and its keyboard (3W4SD-098NDT). To generate a diff of this commit: cvs rdiff -u -r1.34 -r1.35 src/sys/arch/luna68k/dev/lunaws.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/luna68k/dev/lunaws.c diff -u src/sys/arch/luna68k/dev/lunaws.c:1.34 src/sys/arch/luna68k/dev/lunaws.c:1.35 --- src/sys/arch/luna68k/dev/lunaws.c:1.34 Sat Sep 4 18:38:03 2021 +++ src/sys/arch/luna68k/dev/lunaws.c Sat Sep 18 13:44:02 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: lunaws.c,v 1.34 2021/09/04 18:38:03 tsutsui Exp $ */ +/* $NetBSD: lunaws.c,v 1.35 2021/09/18 13:44:02 tsutsui Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ -__KERNEL_RCSID(0, "$NetBSD: lunaws.c,v 1.34 2021/09/04 18:38:03 tsutsui Exp $"); +__KERNEL_RCSID(0, "$NetBSD: lunaws.c,v 1.35 2021/09/18 13:44:02 tsutsui Exp $"); #include "opt_wsdisplay_compat.h" #include "wsmouse.h" @@ -57,10 +57,43 @@ __KERNEL_RCSID(0, "$NetBSD: lunaws.c,v 1 #define OMKBD_RXQ_LEN 64 #define OMKBD_RXQ_LEN_MASK (OMKBD_RXQ_LEN - 1) #define OMKBD_NEXTRXQ(x) (((x) + 1) & OMKBD_RXQ_LEN_MASK) +#define OMKBD_TXQ_LEN 16 +#define OMKBD_TXQ_LEN_MASK (OMKBD_TXQ_LEN - 1) +#define OMKBD_NEXTTXQ(x) (((x) + 1) & OMKBD_TXQ_LEN_MASK) + +/* Keyboard commands */ +/* 000XXXXXb : LED commands */ +#define OMKBD_LED_ON_KANA 0x10 /* kana LED on */ +#define OMKBD_LED_OFF_KANA 0x00 /* kana LED off */ +#define OMKBD_LED_ON_CAPS 0x11 /* caps LED on */ +#define OMKBD_LED_OFF_CAPS 0x01 /* caps LED off */ +/* 010XXXXXb : buzzer commands */ +#define OMKBD_BUZZER 0x40 +#define OMKBD_BUZZER_PERIOD 0x18 +#define OMKBD_BUZZER_40MS 0x00 +#define OMKBD_BUZZER_150MS 0x08 +#define OMKBD_BUZZER_400MS 0x10 +#define OMKBD_BUZZER_700MS 0x18 +#define OMKBD_BUZZER_PITCH 0x07 +#define OMKBD_BUZZER_6000HZ 0x00 +#define OMKBD_BUZZER_3000HZ 0x01 +#define OMKBD_BUZZER_1500HZ 0x02 +#define OMKBD_BUZZER_1000HZ 0x03 +#define OMKBD_BUZZER_600HZ 0x04 +#define OMKBD_BUZZER_300HZ 0x05 +#define OMKBD_BUZZER_150HZ 0x06 +#define OMKBD_BUZZER_100HZ 0x07 +/* 011XXXXXb : mouse on command */ +#define OMKBD_MOUSE_ON 0x60 +/* 001XXXXXb : mouse off command */ +#define OMKBD_MOUSE_OFF 0x20 + +#define OMKBD_BUZZER_DEFAULT \ + (OMKBD_BUZZER | OMKBD_BUZZER_40MS | OMKBD_BUZZER_1500HZ) static const uint8_t ch1_regs[6] = { WR0_RSTINT, /* Reset E/S Interrupt */ - WR1_RXALLS, /* Rx per char, No Tx */ + WR1_RXALLS | WR1_TXENBL, /* Rx per char, Tx */ 0, /* */ WR3_RX8BIT | WR3_RXENBL, /* Rx */ WR4_BAUD96 | WR4_STOP1 | WR4_NPARITY, /* Tx/Rx */ @@ -75,6 +108,12 @@ struct ws_softc { uint8_t sc_rxq[OMKBD_RXQ_LEN]; u_int sc_rxqhead; u_int sc_rxqtail; + uint8_t sc_txq[OMKBD_TXQ_LEN]; + u_int sc_txqhead; + u_int sc_txqtail; + bool sc_tx_busy; + bool sc_tx_done; + int sc_leds; #if NWSMOUSE > 0 device_t sc_wsmousedev; int sc_msbuttons, sc_msdx, sc_msdy; @@ -84,28 +123,36 @@ struct ws_softc { int sc_rawkbd; }; -static void omkbd_input(void *, int); -static void omkbd_decode(void *, int, u_int *, int *); +static void omkbd_input(struct ws_softc *, int); +static void omkbd_send(struct ws_softc *, uint8_t); +static void omkbd_decode(struct ws_softc *, int, u_int *, int *); + static int omkbd_enable(void *, int); static void omkbd_set_leds(void *, int); static int omkbd_ioctl(void *, u_long, void *, int, struct lwp *); +static void omkbd_complex_buzzer(struct ws_softc *, struct wskbd_bell_data *); +static uint8_t omkbd_get_buzcmd(struct ws_softc *, struct wskbd_bell_data *, + uint8_t); + static const struct wskbd_mapdata omkbd_keymapdata = { - omkbd_keydesctab, - KB_JP, + .keydesc = omkbd_keydesctab, + .layout = KB_JP, }; static const struct wskbd_accessops omkbd_accessops = { - omkbd_enable, - omkbd_set_leds, - omkbd_ioctl, + .enable = omkbd_enable, + .set_leds = omkbd_set_leds, + .ioctl = omkbd_ioctl, }; void ws_cnattach(void); static void ws_cngetc(void *, u_int *, int *); static void ws_cnpollc(void *, int); +static void ws_cnbell(void *, u_int, u_int, u_int); static const struct wskbd_consops ws_consops = { - ws_cngetc, - ws_cnpollc, + .getc = ws_cngetc, + .pollc = ws_cnpollc, + .bell = ws_cnbell, }; #if NWSMOUSE > 0 @@ -129,6 +176,18 @@ static void wsattach(device_t, device_t, CFATTACH_DECL_NEW(ws, sizeof(struct ws_softc), wsmatch, wsattach, NULL, NULL); +/* #define LUNAWS_DEBUG */ + +#ifdef LUNAWS_DEBUG +#define DEBUG_KBDTX 0x01 +#define DEBUG_RXSOFT 0x02 +#define DEBUG_BUZZER 0x04 +uint32_t lunaws_debug = 0x00 /* | DEBUG_BUZZER | DEBUG_KBDTX | DEBUG_RXSOFT */; +#define DPRINTF(x, y) if (lunaws_debug & (x)) printf y +#else +#define DPRINTF(x, y) __nothing +#endif + static int wsmatch(device_t parent, cfdata_t cf, void *aux) { @@ -159,17 +218,24 @@ wsattach(device_t parent, device_t self, setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]); setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]); setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]); - setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); - - syscnputc((dev_t)1, 0x20); /* keep quiet mouse */ sc->sc_rxqhead = 0; sc->sc_rxqtail = 0; + sc->sc_txqhead = 0; + sc->sc_txqtail = 0; + sc->sc_tx_busy = false; + sc->sc_tx_done = false; sc->sc_si = softint_establish(SOFTINT_SERIAL, wssoftintr, sc); + /* enable interrupt */ + setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); + aprint_normal("\n"); + /* keep mouse quiet */ + omkbd_send(sc, OMKBD_MOUSE_OFF); + a.console = (args->hwflags == 1); a.keymap = &omkbd_keymapdata; a.accessops = &omkbd_accessops; @@ -189,7 +255,6 @@ wsattach(device_t parent, device_t self, sc->sc_msreport = 0; } -/*ARGSUSED*/ static void wsintr(void *arg) { @@ -197,6 +262,7 @@ wsintr(void *arg) struct sioreg *sio = sc->sc_ctl; uint8_t code; int rr; + bool handled = false; rr = getsiocsr(sio); if ((rr & RR_RXRDY) != 0) { @@ -209,11 +275,18 @@ wsintr(void *arg) sc->sc_rxq[sc->sc_rxqtail] = code; sc->sc_rxqtail = OMKBD_NEXTRXQ(sc->sc_rxqtail); } while (((rr = getsiocsr(sio)) & RR_RXRDY) != 0); - softint_schedule(sc->sc_si); + handled = true; } - if ((rr & RR_TXRDY) != 0) + if ((rr & RR_TXRDY) != 0) { sio->sio_cmd = WR0_RSTPEND; - /* not capable of transmit, yet */ + if (sc->sc_tx_busy) { + sc->sc_tx_busy = false; + sc->sc_tx_done = true; + handled = true; + } + } + if (handled) + softint_schedule(sc->sc_si); } static void @@ -222,8 +295,32 @@ wssoftintr(void *arg) struct ws_softc *sc = arg; uint8_t code; + /* handle pending keyboard commands */ + if (sc->sc_tx_done) { + int s; + + s = splserial(); + sc->sc_tx_done = false; + DPRINTF(DEBUG_KBDTX, ("%s: tx complete\n", __func__)); + if (sc->sc_txqhead != sc->sc_txqtail) { + struct sioreg *sio = sc->sc_ctl; + + sc->sc_tx_busy = true; + sio->sio_data = sc->sc_txq[sc->sc_txqhead]; + DPRINTF(DEBUG_KBDTX, + ("%s: sio_data <- txq[%2d] (%02x)\n", __func__, + sc->sc_txqhead, sc->sc_txq[sc->sc_txqhead])); + + sc->sc_txqhead = OMKBD_NEXTTXQ(sc->sc_txqhead); + } + splx(s); + } + + /* handle received keyboard and mouse data */ while (sc->sc_rxqhead != sc->sc_rxqtail) { code = sc->sc_rxq[sc->sc_rxqhead]; + DPRINTF(DEBUG_RXSOFT, ("%s: %02x <- rxq[%2d]\n", __func__, + code, sc->sc_rxqhead)); sc->sc_rxqhead = OMKBD_NEXTRXQ(sc->sc_rxqhead); /* * if (code >= 0x80 && code <= 0x87), i.e. @@ -265,13 +362,38 @@ wssoftintr(void *arg) } static void -omkbd_input(void *v, int data) +omkbd_send(struct ws_softc *sc, uint8_t txdata) +{ + int s; + + if (!sc->sc_tx_busy) { + struct sioreg *sio = sc->sc_ctl; + + DPRINTF(DEBUG_KBDTX, + ("%s: sio_data <- %02x\n", __func__, txdata)); + s = splserial(); + sc->sc_tx_busy = true; + sio->sio_data = txdata; + splx(s); + } else { + s = splsoftserial(); + sc->sc_txq[sc->sc_txqtail] = txdata; + DPRINTF(DEBUG_KBDTX, + ("%s: txq[%2d] <- %02x\n", __func__, + sc->sc_txqtail, sc->sc_txq[sc->sc_txqtail])); + sc->sc_txqtail = OMKBD_NEXTTXQ(sc->sc_txqtail); + splx(s); + softint_schedule(sc->sc_si); + } +} + +static void +omkbd_input(struct ws_softc *sc, int data) { - struct ws_softc *sc = v; u_int type; int key; - omkbd_decode(v, data, &type, &key); + omkbd_decode(sc, data, &type, &key); #ifdef WSDISPLAY_COMPAT_RAWKBD if (sc->sc_rawkbd) { @@ -299,7 +421,7 @@ omkbd_input(void *v, int data) } static void -omkbd_decode(void *v, int datain, u_int *type, int *dataout) +omkbd_decode(struct ws_softc *sc, int datain, u_int *type, int *dataout) { *type = (datain & 0x80) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; @@ -307,19 +429,98 @@ omkbd_decode(void *v, int datain, u_int } static void -ws_cngetc(void *v, u_int *type, int *data) +omkbd_complex_buzzer(struct ws_softc *sc, struct wskbd_bell_data *wbd) { + uint8_t buzcmd; + + buzcmd = omkbd_get_buzcmd(sc, wbd, OMKBD_BUZZER_DEFAULT); + omkbd_send(sc, buzcmd); +} + +static uint8_t +omkbd_get_buzcmd(struct ws_softc *sc, struct wskbd_bell_data *wbd, + uint8_t obuzcmd) +{ + u_int pitch, period; + uint8_t buzcmd; + + pitch = wbd->pitch; + period = wbd->period; + buzcmd = OMKBD_BUZZER; + + if ((wbd->which & WSKBD_BELL_DOPERIOD) == 0) + buzcmd |= obuzcmd & OMKBD_BUZZER_PERIOD; + else if (period >= 700) + buzcmd |= OMKBD_BUZZER_700MS; + else if (period >= 400) + buzcmd |= OMKBD_BUZZER_400MS; + else if (period >= 150) + buzcmd |= OMKBD_BUZZER_150MS; + else + buzcmd |= OMKBD_BUZZER_40MS; + + if ((wbd->which & WSKBD_BELL_DOPITCH) == 0) + buzcmd |= obuzcmd & OMKBD_BUZZER_PITCH; + else if (pitch >= 6000) + buzcmd |= OMKBD_BUZZER_6000HZ; + else if (pitch >= 3000) + buzcmd |= OMKBD_BUZZER_3000HZ; + else if (pitch >= 1500) + buzcmd |= OMKBD_BUZZER_1500HZ; + else if (pitch >= 1000) + buzcmd |= OMKBD_BUZZER_1000HZ; + else if (pitch >= 600) + buzcmd |= OMKBD_BUZZER_600HZ; + else if (pitch >= 300) + buzcmd |= OMKBD_BUZZER_300HZ; + else if (pitch >= 150) + buzcmd |= OMKBD_BUZZER_150HZ; + else + buzcmd |= OMKBD_BUZZER_100HZ; + + /* no volume control for buzzer on the LUNA keyboards */ + + return buzcmd; +} + +static void +ws_cngetc(void *cookie, u_int *type, int *data) +{ + struct ws_softc *sc = cookie; int code; code = syscngetc((dev_t)1); - omkbd_decode(v, code, type, data); + omkbd_decode(sc, code, type, data); } static void -ws_cnpollc(void *v, int on) +ws_cnpollc(void *cookie, int on) { } +static void +ws_cnbell(void *cookie, u_int pitch, u_int period, u_int volume) +{ + struct ws_softc *sc = cookie; + struct wskbd_bell_data wbd; + uint8_t buzcmd; + + /* + * XXX cnbell(9) man page should describe each args.. + * (it looks similar to the struct wskbd_bell_data) + * pitch: bell frequency in hertz + * period: bell period in ms + * volume: bell volume as a percentage (0-100) (as spkr(4)) + */ + wbd.which = WSKBD_BELL_DOALL; + wbd.period = period; + wbd.pitch = pitch; + wbd.volume = volume; + buzcmd = omkbd_get_buzcmd(sc, &wbd, OMKBD_BUZZER_DEFAULT); + + syscnputc((dev_t)1, buzcmd); +} + /* EXPORT */ void ws_cnattach(void) { @@ -331,39 +532,67 @@ ws_cnattach(void) } static int -omkbd_enable(void *v, int on) +omkbd_enable(void *cookie, int on) { return 0; } static void -omkbd_set_leds(void *v, int leds) +omkbd_set_leds(void *cookie, int leds) { + struct ws_softc *sc = cookie; + uint8_t ledcmd; -#if 0 - syscnputc((dev_t)1, 0x10); /* kana LED on */ - syscnputc((dev_t)1, 0x00); /* kana LED off */ - syscnputc((dev_t)1, 0x11); /* caps LED on */ - syscnputc((dev_t)1, 0x01); /* caps LED off */ + sc->sc_leds = leds; + if ((leds & WSKBD_LED_CAPS) != 0) { + ledcmd = OMKBD_LED_ON_CAPS; + } else { + ledcmd = OMKBD_LED_OFF_CAPS; + } + omkbd_send(sc, ledcmd); + +#if 0 /* no KANA lock support in wskbd */ + if ((leds & WSKBD_LED_KANA) != 0) { + ledcmd = OMKBD_LED_ON_KANA; + } else #endif + { + ledcmd = OMKBD_LED_OFF_KANA; + } + omkbd_send(sc, ledcmd); } static int -omkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) +omkbd_ioctl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l) { -#ifdef WSDISPLAY_COMPAT_RAWKBD - struct ws_softc *sc = v; -#endif + struct ws_softc *sc = cookie; + struct wskbd_bell_data *wbd; switch (cmd) { case WSKBDIO_GTYPE: *(int *)data = WSKBD_TYPE_LUNA; return 0; case WSKBDIO_SETLEDS: + omkbd_set_leds(cookie, *(int *)data); + return 0; case WSKBDIO_GETLEDS: - case WSKBDIO_COMPLEXBELL: /* XXX capable of complex bell */ + *(int *)data = sc->sc_leds; return 0; + + /* + * Note all WSKBDIO_*BELL ioctl(2)s except WSKBDIO_COMPLEXBELL + * are handled MI wskbd(4) layer. + * (wskbd_displayioctl() in src/sys/dev/wscons/wskbd.c) + */ + case WSKBDIO_COMPLEXBELL: + wbd = data; + DPRINTF(DEBUG_BUZZER, + ("%s: WSKBDIO_COMPLEXBELL: pitch = %d, period = %d\n", + __func__, wbd->pitch, wbd->period)); + omkbd_complex_buzzer(sc, wbd); + return 0; + #ifdef WSDISPLAY_COMPAT_RAWKBD case WSKBDIO_SETMODE: sc->sc_rawkbd = *(int *)data == WSKBD_RAW; @@ -379,15 +608,19 @@ omkbd_ioctl(void *v, u_long cmd, void *d #if NWSMOUSE > 0 static int -omms_enable(void *v) +omms_enable(void *cookie) { - syscnputc((dev_t)1, 0x60); /* enable 3 byte long mouse reporting */ + struct ws_softc *sc = cookie; + + /* enable 3 byte long mouse reporting */ + omkbd_send(sc, OMKBD_MOUSE_ON); + return 0; } /*ARGUSED*/ static int -omms_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) +omms_ioctl(void *cookie, u_long cmd, void *data, int flag, struct lwp *l) { if (cmd == WSMOUSEIO_GTYPE) { @@ -398,8 +631,10 @@ omms_ioctl(void *v, u_long cmd, void *da } static void -omms_disable(void *v) +omms_disable(void *cookie) { - syscnputc((dev_t)1, 0x20); /* quiet mouse */ + struct ws_softc *sc = cookie; + + omkbd_send(sc, OMKBD_MOUSE_OFF); } #endif