1 /* $NetBSD: hilkbd.c,v 1.3 2011/02/21 12:33:05 he 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 int sc_nrep; 68 char sc_rep[HILBUFSIZE * 2]; 69 struct callout sc_rawrepeat_ch; 70 #define REP_DELAY1 400 71 #define REP_DELAYN 100 72 #endif 73 }; 74 75 static int hilkbdprobe(device_t, cfdata_t, void *); 76 static void hilkbdattach(device_t, device_t, void *); 77 static int hilkbddetach(device_t, int); 78 79 CFATTACH_DECL_NEW(hilkbd, sizeof(struct hilkbd_softc), 80 hilkbdprobe, hilkbdattach, hilkbddetach, NULL); 81 82 static int hilkbd_enable(void *, int); 83 static void hilkbd_set_leds(void *, int); 84 static int hilkbd_ioctl(void *, u_long, void *, int, struct lwp *); 85 86 static const struct wskbd_accessops hilkbd_accessops = { 87 hilkbd_enable, 88 hilkbd_set_leds, 89 hilkbd_ioctl, 90 }; 91 92 static void hilkbd_cngetc(void *, u_int *, int *); 93 static void hilkbd_cnpollc(void *, int); 94 static void hilkbd_cnbell(void *, u_int, u_int, u_int); 95 96 static const struct wskbd_consops hilkbd_consops = { 97 hilkbd_cngetc, 98 hilkbd_cnpollc, 99 hilkbd_cnbell, 100 }; 101 102 static struct wskbd_mapdata hilkbd_keymapdata = { 103 hilkbd_keydesctab, 104 #ifdef HILKBD_LAYOUT 105 HILKBD_LAYOUT, 106 #else 107 KB_US, 108 #endif 109 }; 110 111 static struct wskbd_mapdata hilkbd_keymapdata_ps2 = { 112 hilkbd_keydesctab_ps2, 113 #ifdef HILKBD_LAYOUT 114 HILKBD_LAYOUT, 115 #else 116 KB_US, 117 #endif 118 }; 119 120 static void hilkbd_bell(struct hil_softc *, u_int, u_int, u_int); 121 static void hilkbd_callback(struct hildev_softc *, u_int, uint8_t *); 122 static void hilkbd_decode(struct hilkbd_softc *, uint8_t, u_int *, int *, 123 int); 124 static int hilkbd_is_console(int); 125 #ifdef WSDISPLAY_COMPAT_RAWKBD 126 static void hilkbd_rawrepeat(void *); 127 #endif 128 129 static int seen_hilkbd_console; 130 131 int 132 hilkbdprobe(device_t parent, cfdata_t cf, void *aux) 133 { 134 struct hil_attach_args *ha = aux; 135 136 if (ha->ha_type != HIL_DEVICE_KEYBOARD && 137 ha->ha_type != HIL_DEVICE_BUTTONBOX) 138 return 0; 139 140 return 1; 141 } 142 143 void 144 hilkbdattach(device_t parent, device_t self, void *aux) 145 { 146 struct hilkbd_softc *sc = device_private(self); 147 struct hil_attach_args *ha = aux; 148 struct wskbddev_attach_args a; 149 uint8_t layoutcode; 150 int ps2; 151 152 sc->sc_hildev.sc_dev = self; 153 sc->hd_code = ha->ha_code; 154 sc->hd_type = ha->ha_type; 155 sc->hd_infolen = ha->ha_infolen; 156 memcpy(sc->hd_info, ha->ha_info, ha->ha_infolen); 157 sc->hd_fn = hilkbd_callback; 158 159 if (ha->ha_type == HIL_DEVICE_KEYBOARD) { 160 /* 161 * Determine the keyboard language configuration, but don't 162 * override a user-specified setting. 163 */ 164 layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1); 165 #ifndef HILKBD_LAYOUT 166 if (layoutcode < MAXHILKBDLAYOUT && 167 hilkbd_layouts[layoutcode] != -1) 168 hilkbd_keymapdata.layout = 169 hilkbd_keymapdata_ps2.layout = 170 hilkbd_layouts[layoutcode]; 171 #endif 172 173 aprint_normal(", layout %x", layoutcode); 174 } 175 176 /* 177 * Interpret the identification bytes, if any 178 */ 179 if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) { 180 /* HILIOB_PROMPT is not always reported... */ 181 sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4; 182 if (sc->sc_numleds != 0) 183 aprint_normal(", %d leds", sc->sc_numleds); 184 } 185 186 aprint_normal("\n"); 187 188 /* 189 * Red lettered keyboards come in two flavours, the old one 190 * with only one control key, to the left of the escape key, 191 * and the modern one which has a PS/2 like layout, and leds. 192 * 193 * Unfortunately for us, they use the same device ID range. 194 * We'll differentiate them by looking at the leds property. 195 */ 196 ps2 = (sc->sc_numleds != 0); 197 198 #ifdef WSDISPLAY_COMPAT_RAWKBD 199 callout_init(&sc->sc_rawrepeat_ch, 0); 200 callout_setfunc(&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(device_t self, int flags) 234 { 235 struct hilkbd_softc *sc = device_private(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 struct hildev_softc *hdsc = &sc->sc_hildev; 275 int changemask; 276 277 if (sc->sc_numleds == 0) 278 return; 279 280 changemask = leds ^ sc->sc_ledstate; 281 if (changemask == 0) 282 return; 283 284 /* We do not handle more than 3 leds here */ 285 if (changemask & WSKBD_LED_SCROLL) 286 send_hildev_cmd(hdsc, 287 (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1, 288 NULL, NULL); 289 if (changemask & WSKBD_LED_NUM) 290 send_hildev_cmd(hdsc, 291 (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2, 292 NULL, NULL); 293 if (changemask & WSKBD_LED_CAPS) 294 send_hildev_cmd(hdsc, 295 (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3, 296 NULL, NULL); 297 298 sc->sc_ledstate = leds; 299 } 300 301 int 302 hilkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 303 { 304 struct hilkbd_softc *sc = v; 305 306 switch (cmd) { 307 case WSKBDIO_GTYPE: 308 *(int *)data = WSKBD_TYPE_HIL; 309 return 0; 310 case WSKBDIO_SETLEDS: 311 hilkbd_set_leds(v, *(int *)data); 312 return 0; 313 case WSKBDIO_GETLEDS: 314 *(int *)data = sc->sc_ledstate; 315 return 0; 316 #ifdef WSDISPLAY_COMPAT_RAWKBD 317 case WSKBDIO_SETMODE: 318 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 319 callout_stop(&sc->sc_rawrepeat_ch); 320 return 0; 321 #endif 322 case WSKBDIO_COMPLEXBELL: 323 #define d ((struct wskbd_bell_data *)data) 324 hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)), 325 d->pitch, d->period, d->volume); 326 #undef d 327 return 0; 328 } 329 330 return EPASSTHROUGH; 331 } 332 333 void 334 hilkbd_cngetc(void *v, u_int *type, int *data) 335 { 336 struct hilkbd_softc *sc = v; 337 struct hildev_softc *hdsc = &sc->sc_hildev; 338 uint8_t c, stat; 339 340 for (;;) { 341 while (hil_poll_data(hdsc, &stat, &c) != 0) 342 ; 343 344 /* 345 * Disregard keyboard data packet header. 346 * Note that no key generates it, so we're safe. 347 */ 348 if (c != HIL_KBDBUTTON) 349 break; 350 } 351 352 hilkbd_decode(sc, c, type, data, HIL_KBDBUTTON); 353 } 354 355 void 356 hilkbd_cnpollc(void *v, int on) 357 { 358 struct hilkbd_softc *sc = v; 359 360 hil_set_poll(device_private(device_parent(sc->sc_hildev.sc_dev)), on); 361 } 362 363 void 364 hilkbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 365 { 366 struct hilkbd_softc *sc = v; 367 368 hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)), 369 pitch, period, volume); 370 } 371 372 void 373 hilkbd_bell(struct hil_softc *sc, u_int pitch, u_int period, u_int volume) 374 { 375 uint8_t buf[2]; 376 377 /* XXX there could be at least a pitch -> HIL pitch conversion here */ 378 #define BELLDUR 80 /* tone duration in msec (10-2560) */ 379 #define BELLFREQ 8 /* tone frequency (0-63) */ 380 buf[0] = ar_format(period - 10); 381 buf[1] = BELLFREQ; 382 send_hil_cmd(sc, HIL_SETTONE, buf, 2, NULL); 383 } 384 385 void 386 hilkbd_callback(struct hildev_softc *hdsc, u_int buflen, uint8_t *buf) 387 { 388 struct hilkbd_softc *sc = device_private(hdsc->sc_dev); 389 u_int type; 390 int kbdtype, key; 391 int i, s; 392 393 /* 394 * Ignore packet if we don't need it 395 */ 396 if (sc->sc_enabled == 0) 397 return; 398 399 if (buflen == 0) 400 return; 401 switch ((kbdtype = *buf & HIL_KBDDATA)) { 402 case HIL_BUTTONBOX: 403 case HIL_KBDBUTTON: 404 break; 405 default: 406 return; 407 } 408 409 #ifdef WSDISPLAY_COMPAT_RAWKBD 410 if (sc->sc_rawkbd) { 411 uint8_t cbuf[HILBUFSIZE * 2]; 412 int c, j, npress; 413 414 npress = j = 0; 415 for (i = 1, buf++; i < buflen; i++) { 416 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 417 c = hilkbd_raw[key]; 418 if (c == 0) 419 continue; 420 /* fake extended scancode if necessary */ 421 if (c & 0x80) 422 cbuf[j++] = 0xe0; 423 cbuf[j] = c & 0x7f; 424 if (type == WSCONS_EVENT_KEY_UP) 425 cbuf[j] |= 0x80; 426 else { 427 /* remember pressed keys for autorepeat */ 428 if (c & 0x80) 429 sc->sc_rep[npress++] = 0xe0; 430 sc->sc_rep[npress++] = c & 0x7f; 431 } 432 j++; 433 } 434 435 s = spltty(); 436 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 437 splx(s); 438 callout_stop(&sc->sc_rawrepeat_ch); 439 sc->sc_nrep = npress; 440 if (npress != 0) { 441 callout_schedule(&sc->sc_rawrepeat_ch, 442 mstohz(REP_DELAY1)); 443 } 444 } else 445 #endif 446 { 447 s = spltty(); 448 for (i = 1, buf++; i < buflen; i++) { 449 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 450 if (sc->sc_wskbddev != NULL) 451 wskbd_input(sc->sc_wskbddev, type, key); 452 } 453 splx(s); 454 } 455 } 456 457 void 458 hilkbd_decode(struct hilkbd_softc *sc, uint8_t data, u_int *type, int *key, 459 int kbdtype) 460 { 461 462 if (kbdtype == HIL_BUTTONBOX) { 463 if (data == 0x02) /* repeat arrow */ 464 data = sc->sc_lastarrow; 465 else if (data >= 0xf8) 466 sc->sc_lastarrow = data; 467 } 468 469 *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 470 *key = data >> 1; 471 } 472 473 int 474 hilkbd_is_console(int hil_is_console) 475 { 476 477 /* if not first hil keyboard, then not the console */ 478 if (seen_hilkbd_console) 479 return 0; 480 481 /* if PDC console does not match hil bus path, then not the console */ 482 if (hil_is_console == 0) 483 return 0; 484 485 seen_hilkbd_console = 1; 486 return 1; 487 } 488 489 #ifdef WSDISPLAY_COMPAT_RAWKBD 490 void 491 hilkbd_rawrepeat(void *v) 492 { 493 struct hilkbd_softc *sc = v; 494 int s; 495 496 s = spltty(); 497 wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep); 498 splx(s); 499 callout_schedule(&sc->sc_rawrepeat_ch, mstohz(REP_DELAYN)); 500 } 501 #endif 502