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

Reply via email to