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