Home | History | Annotate | Line # | Download | only in hil
hilkbd.c revision 1.6
      1 /*	$NetBSD: hilkbd.c,v 1.6 2021/08/07 16:19:11 thorpej Exp $	*/
      2 /*	$OpenBSD: hilkbd.c,v 1.14 2009/01/21 21:53:59 grange Exp $	*/
      3 /*
      4  * Copyright (c) 2003, Miodrag Vallat.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  *
     28  */
     29 
     30 #include "opt_wsdisplay_compat.h"
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/device.h>
     35 #include <sys/ioctl.h>
     36 #include <sys/kernel.h>
     37 #include <sys/callout.h>
     38 #include <sys/bus.h>
     39 #include <sys/cpu.h>
     40 
     41 #include <machine/autoconf.h>
     42 
     43 #include <dev/hil/hilreg.h>
     44 #include <dev/hil/hilvar.h>
     45 #include <dev/hil/hildevs.h>
     46 
     47 #include <dev/wscons/wsconsio.h>
     48 #include <dev/wscons/wskbdvar.h>
     49 #include <dev/wscons/wsksymdef.h>
     50 #include <dev/wscons/wsksymvar.h>
     51 
     52 #include <dev/hil/hilkbdmap.h>
     53 
     54 struct hilkbd_softc {
     55 	struct hildev_softc sc_hildev;
     56 
     57 	int		sc_numleds;
     58 	int		sc_ledstate;
     59 	int		sc_enabled;
     60 	int		sc_console;
     61 	int		sc_lastarrow;
     62 
     63 	device_t	sc_wskbddev;
     64 
     65 #ifdef WSDISPLAY_COMPAT_RAWKBD
     66 	int		sc_rawkbd;
     67 #endif
     68 };
     69 
     70 static int	hilkbdprobe(device_t, cfdata_t, void *);
     71 static void	hilkbdattach(device_t, device_t, void *);
     72 static int	hilkbddetach(device_t, int);
     73 
     74 CFATTACH_DECL_NEW(hilkbd, sizeof(struct hilkbd_softc),
     75     hilkbdprobe, hilkbdattach, hilkbddetach, NULL);
     76 
     77 static int	hilkbd_enable(void *, int);
     78 static void	hilkbd_set_leds(void *, int);
     79 static int	hilkbd_ioctl(void *, u_long, void *, int, struct lwp *);
     80 
     81 static const struct wskbd_accessops hilkbd_accessops = {
     82 	hilkbd_enable,
     83 	hilkbd_set_leds,
     84 	hilkbd_ioctl,
     85 };
     86 
     87 static void	hilkbd_cngetc(void *, u_int *, int *);
     88 static void	hilkbd_cnpollc(void *, int);
     89 static void	hilkbd_cnbell(void *, u_int, u_int, u_int);
     90 
     91 static const struct wskbd_consops hilkbd_consops = {
     92 	hilkbd_cngetc,
     93 	hilkbd_cnpollc,
     94 	hilkbd_cnbell,
     95 };
     96 
     97 static struct wskbd_mapdata hilkbd_keymapdata = {
     98 	hilkbd_keydesctab,
     99 #ifdef HILKBD_LAYOUT
    100 	HILKBD_LAYOUT,
    101 #else
    102 	KB_US,
    103 #endif
    104 };
    105 
    106 static struct wskbd_mapdata hilkbd_keymapdata_ps2 = {
    107 	hilkbd_keydesctab_ps2,
    108 #ifdef HILKBD_LAYOUT
    109 	HILKBD_LAYOUT,
    110 #else
    111 	KB_US,
    112 #endif
    113 };
    114 
    115 static void	hilkbd_bell(struct hil_softc *, u_int, u_int, u_int);
    116 static void	hilkbd_callback(struct hildev_softc *, u_int, uint8_t *);
    117 static void	hilkbd_decode(struct hilkbd_softc *, uint8_t, u_int *, int *,
    118 		    int);
    119 static int	hilkbd_is_console(int);
    120 
    121 static int	seen_hilkbd_console;
    122 
    123 int
    124 hilkbdprobe(device_t parent, cfdata_t cf, void *aux)
    125 {
    126 	struct hil_attach_args *ha = aux;
    127 
    128 	if (ha->ha_type != HIL_DEVICE_KEYBOARD &&
    129 	    ha->ha_type != HIL_DEVICE_BUTTONBOX)
    130 		return 0;
    131 
    132 	return 1;
    133 }
    134 
    135 void
    136 hilkbdattach(device_t parent, device_t self, void *aux)
    137 {
    138 	struct hilkbd_softc *sc = device_private(self);
    139 	struct hil_attach_args *ha = aux;
    140 	struct wskbddev_attach_args a;
    141 	uint8_t layoutcode;
    142 	int ps2;
    143 
    144 	sc->sc_hildev.sc_dev = self;
    145 	sc->hd_code = ha->ha_code;
    146 	sc->hd_type = ha->ha_type;
    147 	sc->hd_infolen = ha->ha_infolen;
    148 	memcpy(sc->hd_info, ha->ha_info, ha->ha_infolen);
    149 	sc->hd_fn = hilkbd_callback;
    150 
    151 	if (ha->ha_type == HIL_DEVICE_KEYBOARD) {
    152 		/*
    153 		 * Determine the keyboard language configuration, but don't
    154 		 * override a user-specified setting.
    155 		 */
    156 		layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1);
    157 #ifndef HILKBD_LAYOUT
    158 		if (layoutcode < MAXHILKBDLAYOUT &&
    159 		    hilkbd_layouts[layoutcode] != -1)
    160 			hilkbd_keymapdata.layout =
    161 			hilkbd_keymapdata_ps2.layout =
    162 			    hilkbd_layouts[layoutcode];
    163 #endif
    164 
    165 		aprint_normal(", layout %x", layoutcode);
    166 	}
    167 
    168 	/*
    169 	 * Interpret the identification bytes, if any
    170 	 */
    171 	if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) {
    172 		/* HILIOB_PROMPT is not always reported... */
    173 		sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4;
    174 		if (sc->sc_numleds != 0)
    175 			aprint_normal(", %d leds", sc->sc_numleds);
    176 	}
    177 
    178 	aprint_normal("\n");
    179 
    180 	/*
    181 	 * Red lettered keyboards come in two flavours, the old one
    182 	 * with only one control key, to the left of the escape key,
    183 	 * and the modern one which has a PS/2 like layout, and leds.
    184 	 *
    185 	 * Unfortunately for us, they use the same device ID range.
    186 	 * We'll differentiate them by looking at the leds property.
    187 	 */
    188 	ps2 = (sc->sc_numleds != 0);
    189 
    190 	/* Do not consider button boxes as console devices. */
    191 	if (ha->ha_type == HIL_DEVICE_BUTTONBOX)
    192 		a.console = 0;
    193 	else
    194 		a.console = hilkbd_is_console(ha->ha_console);
    195 	a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata;
    196 	a.accessops = &hilkbd_accessops;
    197 	a.accesscookie = sc;
    198 
    199 	if (a.console) {
    200 		sc->sc_console = sc->sc_enabled = 1;
    201 		wskbd_cnattach(&hilkbd_consops, sc, a.keymap);
    202 	} else {
    203 		sc->sc_console = sc->sc_enabled = 0;
    204 	}
    205 
    206 	sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE);
    207 
    208 	/*
    209 	 * If this is an old keyboard with a numeric pad but no ``num lock''
    210 	 * key, simulate it being pressed so that the keyboard runs in
    211 	 * numeric mode.
    212 	 */
    213 	if (!ps2 && sc->sc_wskbddev != NULL) {
    214 		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80);
    215 		wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80);
    216 	}
    217 }
    218 
    219 int
    220 hilkbddetach(device_t self, int flags)
    221 {
    222 	struct hilkbd_softc *sc = device_private(self);
    223 
    224 	/*
    225 	 * Handle console keyboard for the best. It should have been set
    226 	 * as the first device in the loop anyways.
    227 	 */
    228 	if (sc->sc_console) {
    229 		wskbd_cndetach();
    230 		seen_hilkbd_console = 0;
    231 	}
    232 
    233 	if (sc->sc_wskbddev != NULL)
    234 		return config_detach(sc->sc_wskbddev, flags);
    235 
    236 	return 0;
    237 }
    238 
    239 int
    240 hilkbd_enable(void *v, int on)
    241 {
    242 	struct hilkbd_softc *sc = v;
    243 
    244 	if (on) {
    245 		if (sc->sc_enabled)
    246 			return EBUSY;
    247 	} else {
    248 		if (sc->sc_console)
    249 			return EBUSY;
    250 	}
    251 
    252 	sc->sc_enabled = on;
    253 
    254 	return 0;
    255 }
    256 
    257 void
    258 hilkbd_set_leds(void *v, int leds)
    259 {
    260 	struct hilkbd_softc *sc = v;
    261 	struct hildev_softc *hdsc = &sc->sc_hildev;
    262 	int changemask;
    263 
    264 	if (sc->sc_numleds == 0)
    265 		return;
    266 
    267 	changemask = leds ^ sc->sc_ledstate;
    268 	if (changemask == 0)
    269 		return;
    270 
    271 	/* We do not handle more than 3 leds here */
    272 	if (changemask & WSKBD_LED_SCROLL)
    273 		send_hildev_cmd(hdsc,
    274 		    (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1,
    275 		    NULL, NULL);
    276 	if (changemask & WSKBD_LED_NUM)
    277 		send_hildev_cmd(hdsc,
    278 		    (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2,
    279 		    NULL, NULL);
    280 	if (changemask & WSKBD_LED_CAPS)
    281 		send_hildev_cmd(hdsc,
    282 		    (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3,
    283 		    NULL, NULL);
    284 
    285 	sc->sc_ledstate = leds;
    286 }
    287 
    288 int
    289 hilkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
    290 {
    291 	struct hilkbd_softc *sc = v;
    292 
    293 	switch (cmd) {
    294 	case WSKBDIO_GTYPE:
    295 		*(int *)data = WSKBD_TYPE_HIL;
    296 		return 0;
    297 	case WSKBDIO_SETLEDS:
    298 		hilkbd_set_leds(v, *(int *)data);
    299 		return 0;
    300 	case WSKBDIO_GETLEDS:
    301 		*(int *)data = sc->sc_ledstate;
    302 		return 0;
    303 #ifdef WSDISPLAY_COMPAT_RAWKBD
    304 	case WSKBDIO_SETMODE:
    305 		sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
    306 		return 0;
    307 #endif
    308 	case WSKBDIO_COMPLEXBELL:
    309 #define	d ((struct wskbd_bell_data *)data)
    310 		hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)),
    311 		    d->pitch, d->period, d->volume);
    312 #undef d
    313 		return 0;
    314 	}
    315 
    316 	return EPASSTHROUGH;
    317 }
    318 
    319 void
    320 hilkbd_cngetc(void *v, u_int *type, int *data)
    321 {
    322 	struct hilkbd_softc *sc = v;
    323 	struct hildev_softc *hdsc = &sc->sc_hildev;
    324 	uint8_t c, stat;
    325 
    326 	for (;;) {
    327 		while (hil_poll_data(hdsc, &stat, &c) != 0)
    328 			;
    329 
    330 		/*
    331 		 * Disregard keyboard data packet header.
    332 		 * Note that no key generates it, so we're safe.
    333 		 */
    334 		if (c != HIL_KBDBUTTON)
    335 			break;
    336 	}
    337 
    338 	hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON);
    339 }
    340 
    341 void
    342 hilkbd_cnpollc(void *v, int on)
    343 {
    344 	struct hilkbd_softc *sc = v;
    345 
    346 	hil_set_poll(device_private(device_parent(sc->sc_hildev.sc_dev)), on);
    347 }
    348 
    349 void
    350 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume)
    351 {
    352 	struct hilkbd_softc *sc = v;
    353 
    354 	hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)),
    355 	    pitch, period, volume);
    356 }
    357 
    358 void
    359 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume)
    360 {
    361 	uint8_t buf[2];
    362 
    363 	/* XXX there could be at least a pitch -> HIL pitch conversion here */
    364 #define	BELLDUR		80	/* tone duration in msec (10-2560) */
    365 #define	BELLFREQ	8	/* tone frequency (0-63) */
    366 	buf[0] = ar_format(period - 10);
    367 	buf[1] = BELLFREQ;
    368 	send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL);
    369 }
    370 
    371 void
    372 hilkbd_callback(struct hildev_softc *hdsc, u_int buflen, uint8_t *buf)
    373 {
    374 	struct hilkbd_softc *sc = device_private(hdsc->sc_dev);
    375 	u_int type;
    376 	int kbdtype, key;
    377 	int i, s;
    378 
    379 	/*
    380 	 * Ignore packet if we don't need it
    381 	 */
    382 	if (sc->sc_enabled == 0)
    383 		return;
    384 
    385 	if (buflen == 0)
    386 		return;
    387 	switch ((kbdtype = *buf & HIL_KBDDATA)) {
    388 	case HIL_BUTTONBOX:
    389 	case HIL_KBDBUTTON:
    390 		break;
    391 	default:
    392 		return;
    393 	}
    394 
    395 #ifdef WSDISPLAY_COMPAT_RAWKBD
    396 	if (sc->sc_rawkbd) {
    397 		uint8_t cbuf[HILBUFSIZE * 2];
    398 		int c, j;
    399 
    400 		j = 0;
    401 		for (i = 1, buf++; i < buflen; i++) {
    402 			hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
    403 			c = hilkbd_raw[key];
    404 			if (c == 0)
    405 				continue;
    406 			/* fake extended scancode if necessary */
    407 			if (c & 0x80)
    408 				cbuf[j++] = 0xe0;
    409 			cbuf[j] = c & 0x7f;
    410 			if (type == WSCONS_EVENT_KEY_UP)
    411 				cbuf[j] |= 0x80;
    412 			j++;
    413 		}
    414 
    415 		s = spltty();
    416 		wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
    417 		splx(s);
    418 	} else
    419 #endif
    420 	{
    421 		s = spltty();
    422 		for (i = 1, buf++; i < buflen; i++) {
    423 			hilkbd_decode(sc, *buf++, &type, &key, kbdtype);
    424 			if (sc->sc_wskbddev != NULL)
    425 				wskbd_input(sc->sc_wskbddev, type, key);
    426 		}
    427 		splx(s);
    428 	}
    429 }
    430 
    431 void
    432 hilkbd_decode(struct hilkbd_softc *sc, uint8_t data, u_int *type, int *key,
    433     int kbdtype)
    434 {
    435 
    436 	if (kbdtype == HIL_BUTTONBOX) {
    437 		if (data == 0x02)	/* repeat arrow */
    438 			data = sc->sc_lastarrow;
    439 		else if (data >= 0xf8)
    440 			sc->sc_lastarrow = data;
    441 	}
    442 
    443 	*type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
    444 	*key = data >> 1;
    445 }
    446 
    447 int
    448 hilkbd_is_console(int hil_is_console)
    449 {
    450 
    451 	/* if not first hil keyboard, then not the console */
    452 	if (seen_hilkbd_console)
    453 		return 0;
    454 
    455 	/* if PDC console does not match hil bus path, then not the console */
    456 	if (hil_is_console == 0)
    457 		return 0;
    458 
    459 	seen_hilkbd_console = 1;
    460 	return 1;
    461 }
    462