1 /* $OpenBSD: pckbd.c,v 1.2 2001/02/02 20:19:04 aaron Exp $ */ 2 /* $NetBSD: pckbd.c,v 1.24 2000/06/05 22:20:57 sommerfeld Exp $ */ 3 4 /*- 5 * Copyright (c) 1998 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Charles M. Hannum. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /*- 41 * Copyright (c) 1990 The Regents of the University of California. 42 * All rights reserved. 43 * 44 * This code is derived from software contributed to Berkeley by 45 * William Jolitz and Don Ahn. 46 * 47 * Redistribution and use in source and binary forms, with or without 48 * modification, are permitted provided that the following conditions 49 * are met: 50 * 1. Redistributions of source code must retain the above copyright 51 * notice, this list of conditions and the following disclaimer. 52 * 2. Redistributions in binary form must reproduce the above copyright 53 * notice, this list of conditions and the following disclaimer in the 54 * documentation and/or other materials provided with the distribution. 55 * 3. All advertising materials mentioning features or use of this software 56 * must display the following acknowledgement: 57 * This product includes software developed by the University of 58 * California, Berkeley and its contributors. 59 * 4. Neither the name of the University nor the names of its contributors 60 * may be used to endorse or promote products derived from this software 61 * without specific prior written permission. 62 * 63 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 64 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 65 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 66 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 67 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 68 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 69 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 71 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 72 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 73 * SUCH DAMAGE. 74 * 75 * @(#)pccons.c 5.11 (Berkeley) 5/21/91 76 */ 77 78 /* 79 * code to work keyboard for PC-style console 80 */ 81 82 #include <sys/param.h> 83 #include <sys/systm.h> 84 #include <sys/device.h> 85 #include <sys/malloc.h> 86 #include <sys/ioctl.h> 87 88 #include <machine/bus.h> 89 90 #include <dev/isa/isavar.h> /* XXX XXX XXX */ 91 92 #include <dev/ic/pckbcvar.h> 93 94 #include <dev/pckbc/pckbdreg.h> 95 #include <dev/pckbc/pckbdvar.h> 96 #include <dev/pckbc/wskbdmap_mfii.h> 97 98 #include <dev/wscons/wsconsio.h> 99 #include <dev/wscons/wskbdvar.h> 100 #include <dev/wscons/wsksymdef.h> 101 #include <dev/wscons/wsksymvar.h> 102 103 #if defined(__i386__) || defined(__alpha__) 104 #include <sys/kernel.h> /* XXX for hz */ 105 #endif 106 107 struct pckbd_internal { 108 int t_isconsole; 109 pckbc_tag_t t_kbctag; 110 pckbc_slot_t t_kbcslot; 111 112 int t_lastchar; 113 int t_extended; 114 int t_extended1; 115 116 struct pckbd_softc *t_sc; /* back pointer */ 117 }; 118 119 struct pckbd_softc { 120 struct device sc_dev; 121 122 struct pckbd_internal *id; 123 int sc_enabled; 124 125 int sc_ledstate; 126 127 struct device *sc_wskbddev; 128 #ifdef WSDISPLAY_COMPAT_RAWKBD 129 int rawkbd; 130 #endif 131 }; 132 133 static int pckbd_is_console __P((pckbc_tag_t, pckbc_slot_t)); 134 135 int pckbdprobe __P((struct device *, void *, void *)); 136 void pckbdattach __P((struct device *, struct device *, void *)); 137 138 struct cfattach pckbd_ca = { 139 sizeof(struct pckbd_softc), pckbdprobe, pckbdattach, 140 }; 141 142 int pckbd_enable __P((void *, int)); 143 void pckbd_set_leds __P((void *, int)); 144 int pckbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); 145 146 const struct wskbd_accessops pckbd_accessops = { 147 pckbd_enable, 148 pckbd_set_leds, 149 pckbd_ioctl, 150 }; 151 152 void pckbd_cngetc __P((void *, u_int *, int *)); 153 void pckbd_cnpollc __P((void *, int)); 154 void pckbd_cnbell __P((void *, u_int, u_int, u_int)); 155 156 const struct wskbd_consops pckbd_consops = { 157 pckbd_cngetc, 158 pckbd_cnpollc, 159 pckbd_cnbell, 160 }; 161 162 const struct wskbd_mapdata pckbd_keymapdata = { 163 pckbd_keydesctab, 164 #ifdef PCKBD_LAYOUT 165 PCKBD_LAYOUT, 166 #else 167 KB_US, 168 #endif 169 }; 170 171 /* 172 * Hackish support for a bell on the PC Keyboard; when a suitable feeper 173 * is found, it attaches itself into the pckbd driver here. 174 */ 175 void (*pckbd_bell_fn) __P((void *, u_int, u_int, u_int, int)); 176 void *pckbd_bell_fn_arg; 177 178 void pckbd_bell __P((u_int, u_int, u_int, int)); 179 180 int pckbd_set_xtscancode __P((pckbc_tag_t, pckbc_slot_t)); 181 int pckbd_init __P((struct pckbd_internal *, pckbc_tag_t, pckbc_slot_t, 182 int)); 183 void pckbd_input __P((void *, int)); 184 185 static int pckbd_decode __P((struct pckbd_internal *, int, 186 u_int *, int *)); 187 static int pckbd_led_encode __P((int)); 188 static int pckbd_led_decode __P((int)); 189 190 struct pckbd_internal pckbd_consdata; 191 192 int 193 pckbd_set_xtscancode(kbctag, kbcslot) 194 pckbc_tag_t kbctag; 195 pckbc_slot_t kbcslot; 196 { 197 u_char cmd[2]; 198 int res; 199 200 /* 201 * Some keyboard/8042 combinations do not seem to work if the keyboard 202 * is set to table 1; in fact, it would appear that some keyboards just 203 * ignore the command altogether. So by default, we use the AT scan 204 * codes and have the 8042 translate them. Unfortunately, this is 205 * known to not work on some PS/2 machines. We try desparately to deal 206 * with this by checking the (lack of a) translate bit in the 8042 and 207 * attempting to set the keyboard to XT mode. If this all fails, well, 208 * tough luck. 209 * 210 * XXX It would perhaps be a better choice to just use AT scan codes 211 * and not bother with this. 212 */ 213 if (pckbc_xt_translation(kbctag, kbcslot, 1)) { 214 /* The 8042 is translating for us; use AT codes. */ 215 cmd[0] = KBC_SETTABLE; 216 cmd[1] = 2; 217 res = pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); 218 if (res) { 219 u_char cmd[1]; 220 #ifdef DEBUG 221 printf("pckbd: error setting scanset 2\n"); 222 #endif 223 /* 224 * XXX at least one keyboard is reported to lock up 225 * if a "set table" is attempted, thus the "reset". 226 * XXX ignore errors, scanset 2 should be 227 * default anyway. 228 */ 229 cmd[0] = KBC_RESET; 230 (void)pckbc_poll_cmd(kbctag, kbcslot, cmd, 1, 1, 0, 1); 231 pckbc_flush(kbctag, kbcslot); 232 res = 0; 233 } 234 } else { 235 /* Stupid 8042; set keyboard to XT codes. */ 236 cmd[0] = KBC_SETTABLE; 237 cmd[1] = 1; 238 res = pckbc_poll_cmd(kbctag, kbcslot, cmd, 2, 0, 0, 0); 239 #ifdef DEBUG 240 if (res) 241 printf("pckbd: error setting scanset 1\n"); 242 #endif 243 } 244 return (res); 245 } 246 247 static int 248 pckbd_is_console(tag, slot) 249 pckbc_tag_t tag; 250 pckbc_slot_t slot; 251 { 252 return (pckbd_consdata.t_isconsole && 253 (tag == pckbd_consdata.t_kbctag) && 254 (slot == pckbd_consdata.t_kbcslot)); 255 } 256 257 /* 258 * these are both bad jokes 259 */ 260 int 261 pckbdprobe(parent, match, aux) 262 struct device *parent; 263 void *match; 264 void *aux; 265 { 266 struct cfdata *cf = match; 267 struct pckbc_attach_args *pa = aux; 268 u_char cmd[1], resp[1]; 269 int res; 270 271 /* 272 * XXX There are rumours that a keyboard can be connected 273 * to the aux port as well. For me, this didn't work. 274 * For further experiments, allow it if explicitly 275 * wired in the config file. 276 */ 277 if ((pa->pa_slot != PCKBC_KBD_SLOT) && 278 (cf->cf_loc[PCKBCCF_SLOT] == PCKBCCF_SLOT_DEFAULT)) 279 return (0); 280 281 /* Flush any garbage. */ 282 pckbc_flush(pa->pa_tag, pa->pa_slot); 283 284 /* Reset the keyboard. */ 285 cmd[0] = KBC_RESET; 286 res = pckbc_poll_cmd(pa->pa_tag, pa->pa_slot, cmd, 1, 1, resp, 1); 287 if (res) { 288 #ifdef DEBUG 289 printf("pckbdprobe: reset error %d\n", res); 290 #endif 291 /* 292 * There is probably no keyboard connected. 293 * Let the probe succeed if the keyboard is used 294 * as console input - it can be connected later. 295 */ 296 return (pckbd_is_console(pa->pa_tag, pa->pa_slot) ? 1 : 0); 297 } 298 if (resp[0] != KBR_RSTDONE) { 299 printf("pckbdprobe: reset response 0x%x\n", resp[0]); 300 return (0); 301 } 302 303 /* 304 * Some keyboards seem to leave a second ack byte after the reset. 305 * This is kind of stupid, but we account for them anyway by just 306 * flushing the buffer. 307 */ 308 pckbc_flush(pa->pa_tag, pa->pa_slot); 309 310 if (pckbd_set_xtscancode(pa->pa_tag, pa->pa_slot)) 311 return (0); 312 313 return (2); 314 } 315 316 void 317 pckbdattach(parent, self, aux) 318 struct device *parent, *self; 319 void *aux; 320 { 321 struct pckbd_softc *sc = (void *)self; 322 struct pckbc_attach_args *pa = aux; 323 int isconsole; 324 struct wskbddev_attach_args a; 325 326 printf("\n"); 327 328 isconsole = pckbd_is_console(pa->pa_tag, pa->pa_slot); 329 330 if (isconsole) { 331 sc->id = &pckbd_consdata; 332 sc->sc_enabled = 1; 333 } else { 334 u_char cmd[1]; 335 336 sc->id = malloc(sizeof(struct pckbd_internal), 337 M_DEVBUF, M_WAITOK); 338 (void) pckbd_init(sc->id, pa->pa_tag, pa->pa_slot, 0); 339 340 /* no interrupts until enabled */ 341 cmd[0] = KBC_DISABLE; 342 (void) pckbc_poll_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, 343 cmd, 1, 0, 0, 0); 344 sc->sc_enabled = 0; 345 } 346 347 sc->id->t_sc = sc; 348 349 pckbc_set_inputhandler(sc->id->t_kbctag, sc->id->t_kbcslot, 350 pckbd_input, sc, sc->sc_dev.dv_xname); 351 352 a.console = isconsole; 353 354 a.keymap = &pckbd_keymapdata; 355 356 a.accessops = &pckbd_accessops; 357 a.accesscookie = sc; 358 359 /* 360 * Attach the wskbd, saving a handle to it. 361 * XXX XXX XXX 362 */ 363 sc->sc_wskbddev = config_found(self, &a, wskbddevprint); 364 } 365 366 int 367 pckbd_enable(v, on) 368 void *v; 369 int on; 370 { 371 struct pckbd_softc *sc = v; 372 u_char cmd[1]; 373 int res; 374 375 if (on) { 376 if (sc->sc_enabled) 377 return (EBUSY); 378 379 pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 1); 380 381 cmd[0] = KBC_ENABLE; 382 res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, 383 cmd, 1, 0, 1, 0); 384 if (res) { 385 printf("pckbd_enable: command error\n"); 386 return (res); 387 } 388 389 res = pckbd_set_xtscancode(sc->id->t_kbctag, 390 sc->id->t_kbcslot); 391 if (res) 392 return (res); 393 394 sc->sc_enabled = 1; 395 } else { 396 if (sc->id->t_isconsole) 397 return (EBUSY); 398 399 cmd[0] = KBC_DISABLE; 400 res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, 401 cmd, 1, 0, 1, 0); 402 if (res) { 403 printf("pckbd_disable: command error\n"); 404 return (res); 405 } 406 407 pckbc_slot_enable(sc->id->t_kbctag, sc->id->t_kbcslot, 0); 408 409 sc->sc_enabled = 0; 410 } 411 412 return (0); 413 } 414 415 static int 416 pckbd_decode(id, datain, type, dataout) 417 struct pckbd_internal *id; 418 int datain; 419 u_int *type; 420 int *dataout; 421 { 422 int key; 423 424 if (datain == KBR_EXTENDED0) { 425 id->t_extended = 1; 426 return(0); 427 } else if (datain == KBR_EXTENDED1) { 428 id->t_extended1 = 2; 429 return(0); 430 } 431 432 /* map extended keys to (unused) codes 128-254 */ 433 key = (datain & 0x7f) | (id->t_extended ? 0x80 : 0); 434 id->t_extended = 0; 435 436 /* 437 * process BREAK key (EXT1 1D 45 EXT1 9D C5): 438 * map to (unused) code 7F 439 */ 440 if (id->t_extended1 == 2 && (datain == 0x1d || datain == 0x9d)) { 441 id->t_extended1 = 1; 442 return(0); 443 } else if (id->t_extended1 == 1 && 444 (datain == 0x45 || datain == 0xc5)) { 445 id->t_extended1 = 0; 446 key = 0x7f; 447 } else if (id->t_extended1 > 0) { 448 id->t_extended1 = 0; 449 } 450 451 if (datain & 0x80) { 452 id->t_lastchar = 0; 453 *type = WSCONS_EVENT_KEY_UP; 454 } else { 455 /* Always ignore typematic keys */ 456 if (key == id->t_lastchar) 457 return(0); 458 id->t_lastchar = key; 459 *type = WSCONS_EVENT_KEY_DOWN; 460 } 461 462 *dataout = key; 463 return(1); 464 } 465 466 int 467 pckbd_init(t, kbctag, kbcslot, console) 468 struct pckbd_internal *t; 469 pckbc_tag_t kbctag; 470 pckbc_slot_t kbcslot; 471 int console; 472 { 473 bzero(t, sizeof(struct pckbd_internal)); 474 475 t->t_isconsole = console; 476 t->t_kbctag = kbctag; 477 t->t_kbcslot = kbcslot; 478 479 return (pckbd_set_xtscancode(kbctag, kbcslot)); 480 } 481 482 static int 483 pckbd_led_encode(led) 484 int led; 485 { 486 int res; 487 488 res = 0; 489 490 if (led & WSKBD_LED_SCROLL) 491 res |= 0x01; 492 if (led & WSKBD_LED_NUM) 493 res |= 0x02; 494 if (led & WSKBD_LED_CAPS) 495 res |= 0x04; 496 return(res); 497 } 498 499 static int 500 pckbd_led_decode(led) 501 int led; 502 { 503 int res; 504 505 res = 0; 506 if (led & 0x01) 507 res |= WSKBD_LED_SCROLL; 508 if (led & 0x02) 509 res |= WSKBD_LED_NUM; 510 if (led & 0x04) 511 res |= WSKBD_LED_CAPS; 512 return(res); 513 } 514 515 void 516 pckbd_set_leds(v, leds) 517 void *v; 518 int leds; 519 { 520 struct pckbd_softc *sc = v; 521 u_char cmd[2]; 522 523 cmd[0] = KBC_MODEIND; 524 cmd[1] = pckbd_led_encode(leds); 525 sc->sc_ledstate = cmd[1]; 526 527 (void) pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, 528 cmd, 2, 0, 0, 0); 529 } 530 531 /* 532 * Got a console receive interrupt - 533 * the console processor wants to give us a character. 534 */ 535 void 536 pckbd_input(vsc, data) 537 void *vsc; 538 int data; 539 { 540 struct pckbd_softc *sc = vsc; 541 int type, key; 542 543 #ifdef WSDISPLAY_COMPAT_RAWKBD 544 if (sc->rawkbd) { 545 char d = data; 546 wskbd_rawinput(sc->sc_wskbddev, &d, 1); 547 return; 548 } 549 #endif 550 if (pckbd_decode(sc->id, data, &type, &key)) 551 wskbd_input(sc->sc_wskbddev, type, key); 552 } 553 554 int 555 pckbd_ioctl(v, cmd, data, flag, p) 556 void *v; 557 u_long cmd; 558 caddr_t data; 559 int flag; 560 struct proc *p; 561 { 562 struct pckbd_softc *sc = v; 563 564 switch (cmd) { 565 case WSKBDIO_GTYPE: 566 *(int *)data = WSKBD_TYPE_PC_XT; 567 return 0; 568 case WSKBDIO_SETLEDS: { 569 char cmd[2]; 570 int res; 571 cmd[0] = KBC_MODEIND; 572 cmd[1] = pckbd_led_encode(*(int *)data); 573 sc->sc_ledstate = cmd[1]; 574 res = pckbc_enqueue_cmd(sc->id->t_kbctag, sc->id->t_kbcslot, 575 cmd, 2, 0, 1, 0); 576 return (res); 577 } 578 case WSKBDIO_GETLEDS: 579 *(int *)data = pckbd_led_decode(sc->sc_ledstate); 580 return (0); 581 case WSKBDIO_COMPLEXBELL: 582 #define d ((struct wskbd_bell_data *)data) 583 /* 584 * Keyboard can't beep directly; we have an 585 * externally-provided global hook to do this. 586 */ 587 pckbd_bell(d->pitch, d->period, d->volume, 0); 588 #undef d 589 return (0); 590 #ifdef WSDISPLAY_COMPAT_RAWKBD 591 case WSKBDIO_SETMODE: 592 sc->rawkbd = (*(int *)data == WSKBD_RAW); 593 return (0); 594 #endif 595 } 596 return -1; 597 } 598 599 void 600 pckbd_bell(pitch, period, volume, poll) 601 u_int pitch, period, volume; 602 int poll; 603 { 604 605 if (pckbd_bell_fn != NULL) 606 (*pckbd_bell_fn)(pckbd_bell_fn_arg, pitch, period, 607 volume, poll); 608 } 609 610 void 611 pckbd_hookup_bell(fn, arg) 612 void (*fn) __P((void *, u_int, u_int, u_int, int)); 613 void *arg; 614 { 615 616 if (pckbd_bell_fn == NULL) { 617 pckbd_bell_fn = fn; 618 pckbd_bell_fn_arg = arg; 619 } 620 } 621 622 int 623 pckbd_cnattach(kbctag, kbcslot) 624 pckbc_tag_t kbctag; 625 int kbcslot; 626 { 627 char cmd[1]; 628 int res; 629 630 res = pckbd_init(&pckbd_consdata, kbctag, kbcslot, 1); 631 #if 0 /* we allow the console to be attached if no keyboard is present */ 632 if (res) 633 return (res); 634 #endif 635 636 /* Just to be sure. */ 637 cmd[0] = KBC_ENABLE; 638 res = pckbc_poll_cmd(kbctag, kbcslot, cmd, 1, 0, 0, 0); 639 #if 0 640 if (res) 641 return (res); 642 #endif 643 644 wskbd_cnattach(&pckbd_consops, &pckbd_consdata, &pckbd_keymapdata); 645 646 return (0); 647 } 648 649 /* ARGSUSED */ 650 void 651 pckbd_cngetc(v, type, data) 652 void *v; 653 u_int *type; 654 int *data; 655 { 656 struct pckbd_internal *t = v; 657 int val; 658 659 for (;;) { 660 val = pckbc_poll_data(t->t_kbctag, t->t_kbcslot); 661 if ((val != -1) && pckbd_decode(t, val, type, data)) 662 return; 663 } 664 } 665 666 void 667 pckbd_cnpollc(v, on) 668 void *v; 669 int on; 670 { 671 struct pckbd_internal *t = v; 672 673 pckbc_set_poll(t->t_kbctag, t->t_kbcslot, on); 674 } 675 676 void 677 pckbd_cnbell(v, pitch, period, volume) 678 void *v; 679 u_int pitch, period, volume; 680 { 681 682 pckbd_bell(pitch, period, volume, 1); 683 } 684 685 struct cfdriver pckbd_cd = { 686 NULL, "pckbd", DV_DULL 687 }; 688