1 /* $NetBSD: hilkbd.c,v 1.7 2021/09/19 04:55:58 tsutsui 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 .enable = hilkbd_enable, 83 .set_leds = hilkbd_set_leds, 84 .ioctl = 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 .getc = hilkbd_cngetc, 93 .pollc = hilkbd_cnpollc, 94 .bell = hilkbd_cnbell, 95 }; 96 97 static struct wskbd_mapdata hilkbd_keymapdata = { 98 .keydesc = hilkbd_keydesctab, 99 .layout = 100 #ifdef HILKBD_LAYOUT 101 HILKBD_LAYOUT, 102 #else 103 KB_US, 104 #endif 105 }; 106 107 static struct wskbd_mapdata hilkbd_keymapdata_ps2 = { 108 .keydesc = hilkbd_keydesctab_ps2, 109 .layout = 110 #ifdef HILKBD_LAYOUT 111 HILKBD_LAYOUT, 112 #else 113 KB_US, 114 #endif 115 }; 116 117 static void hilkbd_bell(struct hil_softc *, u_int, u_int, u_int); 118 static void hilkbd_callback(struct hildev_softc *, u_int, uint8_t *); 119 static void hilkbd_decode(struct hilkbd_softc *, uint8_t, u_int *, int *, 120 int); 121 static int hilkbd_is_console(int); 122 123 static int seen_hilkbd_console; 124 125 int 126 hilkbdprobe(device_t parent, cfdata_t cf, void *aux) 127 { 128 struct hil_attach_args *ha = aux; 129 130 if (ha->ha_type != HIL_DEVICE_KEYBOARD && 131 ha->ha_type != HIL_DEVICE_BUTTONBOX) 132 return 0; 133 134 return 1; 135 } 136 137 void 138 hilkbdattach(device_t parent, device_t self, void *aux) 139 { 140 struct hilkbd_softc *sc = device_private(self); 141 struct hil_attach_args *ha = aux; 142 struct wskbddev_attach_args a; 143 uint8_t layoutcode; 144 int ps2; 145 146 sc->sc_hildev.sc_dev = self; 147 sc->hd_code = ha->ha_code; 148 sc->hd_type = ha->ha_type; 149 sc->hd_infolen = ha->ha_infolen; 150 memcpy(sc->hd_info, ha->ha_info, ha->ha_infolen); 151 sc->hd_fn = hilkbd_callback; 152 153 if (ha->ha_type == HIL_DEVICE_KEYBOARD) { 154 /* 155 * Determine the keyboard language configuration, but don't 156 * override a user-specified setting. 157 */ 158 layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1); 159 #ifndef HILKBD_LAYOUT 160 if (layoutcode < MAXHILKBDLAYOUT && 161 hilkbd_layouts[layoutcode] != -1) 162 hilkbd_keymapdata.layout = 163 hilkbd_keymapdata_ps2.layout = 164 hilkbd_layouts[layoutcode]; 165 #endif 166 167 aprint_normal(", layout %x", layoutcode); 168 } 169 170 /* 171 * Interpret the identification bytes, if any 172 */ 173 if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) { 174 /* HILIOB_PROMPT is not always reported... */ 175 sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4; 176 if (sc->sc_numleds != 0) 177 aprint_normal(", %d leds", sc->sc_numleds); 178 } 179 180 aprint_normal("\n"); 181 182 /* 183 * Red lettered keyboards come in two flavours, the old one 184 * with only one control key, to the left of the escape key, 185 * and the modern one which has a PS/2 like layout, and leds. 186 * 187 * Unfortunately for us, they use the same device ID range. 188 * We'll differentiate them by looking at the leds property. 189 */ 190 ps2 = (sc->sc_numleds != 0); 191 192 /* Do not consider button boxes as console devices. */ 193 if (ha->ha_type == HIL_DEVICE_BUTTONBOX) 194 a.console = 0; 195 else 196 a.console = hilkbd_is_console(ha->ha_console); 197 a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata; 198 a.accessops = &hilkbd_accessops; 199 a.accesscookie = sc; 200 201 if (a.console) { 202 sc->sc_console = sc->sc_enabled = 1; 203 wskbd_cnattach(&hilkbd_consops, sc, a.keymap); 204 } else { 205 sc->sc_console = sc->sc_enabled = 0; 206 } 207 208 sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE); 209 210 /* 211 * If this is an old keyboard with a numeric pad but no ``num lock'' 212 * key, simulate it being pressed so that the keyboard runs in 213 * numeric mode. 214 */ 215 if (!ps2 && sc->sc_wskbddev != NULL) { 216 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80); 217 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80); 218 } 219 } 220 221 int 222 hilkbddetach(device_t self, int flags) 223 { 224 struct hilkbd_softc *sc = device_private(self); 225 226 /* 227 * Handle console keyboard for the best. It should have been set 228 * as the first device in the loop anyways. 229 */ 230 if (sc->sc_console) { 231 wskbd_cndetach(); 232 seen_hilkbd_console = 0; 233 } 234 235 if (sc->sc_wskbddev != NULL) 236 return config_detach(sc->sc_wskbddev, flags); 237 238 return 0; 239 } 240 241 int 242 hilkbd_enable(void *v, int on) 243 { 244 struct hilkbd_softc *sc = v; 245 246 if (on) { 247 if (sc->sc_enabled) 248 return EBUSY; 249 } else { 250 if (sc->sc_console) 251 return EBUSY; 252 } 253 254 sc->sc_enabled = on; 255 256 return 0; 257 } 258 259 void 260 hilkbd_set_leds(void *v, int leds) 261 { 262 struct hilkbd_softc *sc = v; 263 struct hildev_softc *hdsc = &sc->sc_hildev; 264 int changemask; 265 266 if (sc->sc_numleds == 0) 267 return; 268 269 changemask = leds ^ sc->sc_ledstate; 270 if (changemask == 0) 271 return; 272 273 /* We do not handle more than 3 leds here */ 274 if (changemask & WSKBD_LED_SCROLL) 275 send_hildev_cmd(hdsc, 276 (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1, 277 NULL, NULL); 278 if (changemask & WSKBD_LED_NUM) 279 send_hildev_cmd(hdsc, 280 (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2, 281 NULL, NULL); 282 if (changemask & WSKBD_LED_CAPS) 283 send_hildev_cmd(hdsc, 284 (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3, 285 NULL, NULL); 286 287 sc->sc_ledstate = leds; 288 } 289 290 int 291 hilkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 292 { 293 struct hilkbd_softc *sc = v; 294 295 switch (cmd) { 296 case WSKBDIO_GTYPE: 297 *(int *)data = WSKBD_TYPE_HIL; 298 return 0; 299 case WSKBDIO_SETLEDS: 300 hilkbd_set_leds(v, *(int *)data); 301 return 0; 302 case WSKBDIO_GETLEDS: 303 *(int *)data = sc->sc_ledstate; 304 return 0; 305 #ifdef WSDISPLAY_COMPAT_RAWKBD 306 case WSKBDIO_SETMODE: 307 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 308 return 0; 309 #endif 310 case WSKBDIO_COMPLEXBELL: 311 #define d ((struct wskbd_bell_data *)data) 312 hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)), 313 d->pitch, d->period, d->volume); 314 #undef d 315 return 0; 316 } 317 318 return EPASSTHROUGH; 319 } 320 321 void 322 hilkbd_cngetc(void *v, u_int *type, int *data) 323 { 324 struct hilkbd_softc *sc = v; 325 struct hildev_softc *hdsc = &sc->sc_hildev; 326 uint8_t c, stat; 327 328 for (;;) { 329 while (hil_poll_data(hdsc, &stat, &c) != 0) 330 ; 331 332 /* 333 * Disregard keyboard data packet header. 334 * Note that no key generates it, so we're safe. 335 */ 336 if (c != HIL_KBDBUTTON) 337 break; 338 } 339 340 hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON); 341 } 342 343 void 344 hilkbd_cnpollc(void *v, int on) 345 { 346 struct hilkbd_softc *sc = v; 347 348 hil_set_poll(device_private(device_parent(sc->sc_hildev.sc_dev)), on); 349 } 350 351 void 352 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 353 { 354 struct hilkbd_softc *sc = v; 355 356 hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)), 357 pitch, period, volume); 358 } 359 360 void 361 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume) 362 { 363 uint8_t buf[2]; 364 365 /* XXX there could be at least a pitch -> HIL pitch conversion here */ 366 #define BELLDUR 80 /* tone duration in msec (10-2560) */ 367 #define BELLFREQ 8 /* tone frequency (0-63) */ 368 buf[0] = ar_format(period - 10); 369 buf[1] = BELLFREQ; 370 send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL); 371 } 372 373 void 374 hilkbd_callback(struct hildev_softc *hdsc, u_int buflen, uint8_t *buf) 375 { 376 struct hilkbd_softc *sc = device_private(hdsc->sc_dev); 377 u_int type; 378 int kbdtype, key; 379 int i, s; 380 381 /* 382 * Ignore packet if we don't need it 383 */ 384 if (sc->sc_enabled == 0) 385 return; 386 387 if (buflen == 0) 388 return; 389 switch ((kbdtype = *buf & HIL_KBDDATA)) { 390 case HIL_BUTTONBOX: 391 case HIL_KBDBUTTON: 392 break; 393 default: 394 return; 395 } 396 397 #ifdef WSDISPLAY_COMPAT_RAWKBD 398 if (sc->sc_rawkbd) { 399 uint8_t cbuf[HILBUFSIZE * 2]; 400 int c, j; 401 402 j = 0; 403 for (i = 1, buf++; i < buflen; i++) { 404 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 405 c = hilkbd_raw[key]; 406 if (c == 0) 407 continue; 408 /* fake extended scancode if necessary */ 409 if (c & 0x80) 410 cbuf[j++] = 0xe0; 411 cbuf[j] = c & 0x7f; 412 if (type == WSCONS_EVENT_KEY_UP) 413 cbuf[j] |= 0x80; 414 j++; 415 } 416 417 s = spltty(); 418 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 419 splx(s); 420 } else 421 #endif 422 { 423 s = spltty(); 424 for (i = 1, buf++; i < buflen; i++) { 425 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 426 if (sc->sc_wskbddev != NULL) 427 wskbd_input(sc->sc_wskbddev, type, key); 428 } 429 splx(s); 430 } 431 } 432 433 void 434 hilkbd_decode(struct hilkbd_softc *sc, uint8_t data, u_int *type, int *key, 435 int kbdtype) 436 { 437 438 if (kbdtype == HIL_BUTTONBOX) { 439 if (data == 0x02) /* repeat arrow */ 440 data = sc->sc_lastarrow; 441 else if (data >= 0xf8) 442 sc->sc_lastarrow = data; 443 } 444 445 *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 446 *key = data >> 1; 447 } 448 449 int 450 hilkbd_is_console(int hil_is_console) 451 { 452 453 /* if not first hil keyboard, then not the console */ 454 if (seen_hilkbd_console) 455 return 0; 456 457 /* if PDC console does not match hil bus path, then not the console */ 458 if (hil_is_console == 0) 459 return 0; 460 461 seen_hilkbd_console = 1; 462 return 1; 463 } 464