1 /* $NetBSD: hilkbd.c,v 1.5 2021/04/24 23:36:54 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, CFARG_EOL); 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