1 /* $NetBSD: hilkbd.c,v 1.2 2011/02/15 11:05:51 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 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 static void hilkbd_rawrepeat(void *); 126 127 static int seen_hilkbd_console; 128 129 int 130 hilkbdprobe(device_t parent, cfdata_t cf, void *aux) 131 { 132 struct hil_attach_args *ha = aux; 133 134 if (ha->ha_type != HIL_DEVICE_KEYBOARD && 135 ha->ha_type != HIL_DEVICE_BUTTONBOX) 136 return 0; 137 138 return 1; 139 } 140 141 void 142 hilkbdattach(device_t parent, device_t self, void *aux) 143 { 144 struct hilkbd_softc *sc = device_private(self); 145 struct hil_attach_args *ha = aux; 146 struct wskbddev_attach_args a; 147 uint8_t layoutcode; 148 int ps2; 149 150 sc->sc_hildev.sc_dev = self; 151 sc->hd_code = ha->ha_code; 152 sc->hd_type = ha->ha_type; 153 sc->hd_infolen = ha->ha_infolen; 154 memcpy(sc->hd_info, ha->ha_info, ha->ha_infolen); 155 sc->hd_fn = hilkbd_callback; 156 157 if (ha->ha_type == HIL_DEVICE_KEYBOARD) { 158 /* 159 * Determine the keyboard language configuration, but don't 160 * override a user-specified setting. 161 */ 162 layoutcode = ha->ha_id & (MAXHILKBDLAYOUT - 1); 163 #ifndef HILKBD_LAYOUT 164 if (layoutcode < MAXHILKBDLAYOUT && 165 hilkbd_layouts[layoutcode] != -1) 166 hilkbd_keymapdata.layout = 167 hilkbd_keymapdata_ps2.layout = 168 hilkbd_layouts[layoutcode]; 169 #endif 170 171 aprint_normal(", layout %x", layoutcode); 172 } 173 174 /* 175 * Interpret the identification bytes, if any 176 */ 177 if (ha->ha_infolen > 2 && (ha->ha_info[1] & HIL_IOB) != 0) { 178 /* HILIOB_PROMPT is not always reported... */ 179 sc->sc_numleds = (ha->ha_info[2] & HILIOB_PMASK) >> 4; 180 if (sc->sc_numleds != 0) 181 aprint_normal(", %d leds", sc->sc_numleds); 182 } 183 184 aprint_normal("\n"); 185 186 /* 187 * Red lettered keyboards come in two flavours, the old one 188 * with only one control key, to the left of the escape key, 189 * and the modern one which has a PS/2 like layout, and leds. 190 * 191 * Unfortunately for us, they use the same device ID range. 192 * We'll differentiate them by looking at the leds property. 193 */ 194 ps2 = (sc->sc_numleds != 0); 195 196 #ifdef WSDISPLAY_COMPAT_RAWKBD 197 callout_init(&sc->sc_rawrepeat_ch, 0); 198 callout_setfunc(&sc->sc_rawrepeat_ch, hilkbd_rawrepeat, sc); 199 #endif 200 201 /* Do not consider button boxes as console devices. */ 202 if (ha->ha_type == HIL_DEVICE_BUTTONBOX) 203 a.console = 0; 204 else 205 a.console = hilkbd_is_console(ha->ha_console); 206 a.keymap = ps2 ? &hilkbd_keymapdata_ps2 : &hilkbd_keymapdata; 207 a.accessops = &hilkbd_accessops; 208 a.accesscookie = sc; 209 210 if (a.console) { 211 sc->sc_console = sc->sc_enabled = 1; 212 wskbd_cnattach(&hilkbd_consops, sc, a.keymap); 213 } else { 214 sc->sc_console = sc->sc_enabled = 0; 215 } 216 217 sc->sc_wskbddev = config_found(self, &a, wskbddevprint); 218 219 /* 220 * If this is an old keyboard with a numeric pad but no ``num lock'' 221 * key, simulate it being pressed so that the keyboard runs in 222 * numeric mode. 223 */ 224 if (!ps2 && sc->sc_wskbddev != NULL) { 225 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_DOWN, 80); 226 wskbd_input(sc->sc_wskbddev, WSCONS_EVENT_KEY_UP, 80); 227 } 228 } 229 230 int 231 hilkbddetach(device_t self, int flags) 232 { 233 struct hilkbd_softc *sc = device_private(self); 234 235 /* 236 * Handle console keyboard for the best. It should have been set 237 * as the first device in the loop anyways. 238 */ 239 if (sc->sc_console) { 240 wskbd_cndetach(); 241 seen_hilkbd_console = 0; 242 } 243 244 if (sc->sc_wskbddev != NULL) 245 return config_detach(sc->sc_wskbddev, flags); 246 247 return 0; 248 } 249 250 int 251 hilkbd_enable(void *v, int on) 252 { 253 struct hilkbd_softc *sc = v; 254 255 if (on) { 256 if (sc->sc_enabled) 257 return EBUSY; 258 } else { 259 if (sc->sc_console) 260 return EBUSY; 261 } 262 263 sc->sc_enabled = on; 264 265 return 0; 266 } 267 268 void 269 hilkbd_set_leds(void *v, int leds) 270 { 271 struct hilkbd_softc *sc = v; 272 struct hildev_softc *hdsc = &sc->sc_hildev; 273 int changemask; 274 275 if (sc->sc_numleds == 0) 276 return; 277 278 changemask = leds ^ sc->sc_ledstate; 279 if (changemask == 0) 280 return; 281 282 /* We do not handle more than 3 leds here */ 283 if (changemask & WSKBD_LED_SCROLL) 284 send_hildev_cmd(hdsc, 285 (leds & WSKBD_LED_SCROLL) ? HIL_PROMPT1 : HIL_ACK1, 286 NULL, NULL); 287 if (changemask & WSKBD_LED_NUM) 288 send_hildev_cmd(hdsc, 289 (leds & WSKBD_LED_NUM) ? HIL_PROMPT2 : HIL_ACK2, 290 NULL, NULL); 291 if (changemask & WSKBD_LED_CAPS) 292 send_hildev_cmd(hdsc, 293 (leds & WSKBD_LED_CAPS) ? HIL_PROMPT3 : HIL_ACK3, 294 NULL, NULL); 295 296 sc->sc_ledstate = leds; 297 } 298 299 int 300 hilkbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 301 { 302 struct hilkbd_softc *sc = v; 303 304 switch (cmd) { 305 case WSKBDIO_GTYPE: 306 *(int *)data = WSKBD_TYPE_HIL; 307 return 0; 308 case WSKBDIO_SETLEDS: 309 hilkbd_set_leds(v, *(int *)data); 310 return 0; 311 case WSKBDIO_GETLEDS: 312 *(int *)data = sc->sc_ledstate; 313 return 0; 314 #ifdef WSDISPLAY_COMPAT_RAWKBD 315 case WSKBDIO_SETMODE: 316 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 317 callout_stop(&sc->sc_rawrepeat_ch); 318 return 0; 319 #endif 320 case WSKBDIO_COMPLEXBELL: 321 #define d ((struct wskbd_bell_data *)data) 322 hilkbd_bell(device_private(device_parent(sc->sc_hildev.sc_dev)), 323 d->pitch, d->period, d->volume); 324 #undef d 325 return 0; 326 } 327 328 return EPASSTHROUGH; 329 } 330 331 void 332 hilkbd_cngetc(void *v, u_int *type, int *data) 333 { 334 struct hilkbd_softc *sc = v; 335 struct hildev_softc *hdsc = &sc->sc_hildev; 336 uint8_t c, stat; 337 338 for (;;) { 339 while (hil_poll_data(hdsc, &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(device_private(device_parent(sc->sc_hildev.sc_dev)), 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(device_private(device_parent(sc->sc_hildev.sc_dev)), 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 uint8_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 *hdsc, u_int buflen, uint8_t *buf) 385 { 386 struct hilkbd_softc *sc = device_private(hdsc->sc_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 uint8_t 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 == 0) 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 callout_stop(&sc->sc_rawrepeat_ch); 437 sc->sc_nrep = npress; 438 if (npress != 0) { 439 callout_schedule(&sc->sc_rawrepeat_ch, 440 mstohz(REP_DELAY1)); 441 } 442 } else 443 #endif 444 { 445 s = spltty(); 446 for (i = 1, buf++; i < buflen; i++) { 447 hilkbd_decode(sc, *buf++, &type, &key, kbdtype); 448 if (sc->sc_wskbddev != NULL) 449 wskbd_input(sc->sc_wskbddev, type, key); 450 } 451 splx(s); 452 } 453 } 454 455 void 456 hilkbd_decode(struct hilkbd_softc *sc, uint8_t data, u_int *type, int *key, 457 int kbdtype) 458 { 459 460 if (kbdtype == HIL_BUTTONBOX) { 461 if (data == 0x02) /* repeat arrow */ 462 data = sc->sc_lastarrow; 463 else if (data >= 0xf8) 464 sc->sc_lastarrow = data; 465 } 466 467 *type = (data & 1) ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN; 468 *key = data >> 1; 469 } 470 471 int 472 hilkbd_is_console(int hil_is_console) 473 { 474 475 /* if not first hil keyboard, then not the console */ 476 if (seen_hilkbd_console) 477 return 0; 478 479 /* if PDC console does not match hil bus path, then not the console */ 480 if (hil_is_console == 0) 481 return 0; 482 483 seen_hilkbd_console = 1; 484 return 1; 485 } 486 487 #ifdef WSDISPLAY_COMPAT_RAWKBD 488 void 489 hilkbd_rawrepeat(void *v) 490 { 491 struct hilkbd_softc *sc = v; 492 int s; 493 494 s = spltty(); 495 wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep); 496 splx(s); 497 callout_schedule(&sc->sc_rawrepeat_ch, mstohz(REP_DELAYN)); 498 } 499 #endif 500