1 /* $NetBSD: akbd.c,v 1.29 2002/10/02 05:30:39 thorpej Exp $ */ 2 3 /* 4 * Copyright (C) 1998 Colin Wood 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Colin Wood. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/device.h> 35 #include <sys/fcntl.h> 36 #include <sys/poll.h> 37 #include <sys/select.h> 38 #include <sys/proc.h> 39 #include <sys/signalvar.h> 40 #include <sys/systm.h> 41 42 #include <dev/wscons/wsconsio.h> 43 #include <dev/wscons/wskbdvar.h> 44 #include <dev/wscons/wsksymdef.h> 45 #include <dev/wscons/wsksymvar.h> 46 #include <dev/ofw/openfirm.h> 47 48 #include <machine/autoconf.h> 49 #define KEYBOARD_ARRAY 50 #include <machine/keyboard.h> 51 52 #include <macppc/dev/adbvar.h> 53 #include <macppc/dev/aedvar.h> 54 #include <macppc/dev/akbdmap.h> 55 #include <macppc/dev/akbdvar.h> 56 #include <macppc/dev/pm_direct.h> 57 58 #include "aed.h" 59 60 /* 61 * Function declarations. 62 */ 63 static int akbdmatch __P((struct device *, struct cfdata *, void *)); 64 static void akbdattach __P((struct device *, struct device *, void *)); 65 void kbd_adbcomplete __P((caddr_t buffer, caddr_t data_area, int adb_command)); 66 static void kbd_processevent __P((adb_event_t *event, struct akbd_softc *)); 67 static void kbd_passup __P((struct akbd_softc *sc, int)); 68 #ifdef notyet 69 static u_char getleds __P((int)); 70 static int setleds __P((struct akbd_softc *, u_char)); 71 static void blinkleds __P((struct akbd_softc *)); 72 #endif 73 74 /* Driver definition. */ 75 CFATTACH_DECL(akbd, sizeof(struct akbd_softc), 76 akbdmatch, akbdattach, NULL, NULL); 77 78 extern struct cfdriver akbd_cd; 79 80 int akbd_enable __P((void *, int)); 81 void akbd_set_leds __P((void *, int)); 82 int akbd_ioctl __P((void *, u_long, caddr_t, int, struct proc *)); 83 84 struct wskbd_accessops akbd_accessops = { 85 akbd_enable, 86 akbd_set_leds, 87 akbd_ioctl, 88 }; 89 90 void akbd_cngetc __P((void *, u_int *, int *)); 91 void akbd_cnpollc __P((void *, int)); 92 93 struct wskbd_consops akbd_consops = { 94 akbd_cngetc, 95 akbd_cnpollc, 96 }; 97 98 struct wskbd_mapdata akbd_keymapdata = { 99 akbd_keydesctab, 100 #ifdef AKBD_LAYOUT 101 AKBD_LAYOUT, 102 #else 103 KB_US, 104 #endif 105 }; 106 107 static int akbd_is_console; 108 static int akbd_console_attached; 109 static int pcmcia_soft_eject; 110 111 static int 112 akbdmatch(parent, cf, aux) 113 struct device *parent; 114 struct cfdata *cf; 115 void *aux; 116 { 117 struct adb_attach_args *aa_args = aux; 118 119 if (aa_args->origaddr == ADBADDR_KBD) 120 return 1; 121 else 122 return 0; 123 } 124 125 static void 126 akbdattach(parent, self, aux) 127 struct device *parent, *self; 128 void *aux; 129 { 130 ADBSetInfoBlock adbinfo; 131 struct akbd_softc *sc = (struct akbd_softc *)self; 132 struct adb_attach_args *aa_args = aux; 133 int error, kbd_done; 134 short cmd; 135 u_char buffer[9]; 136 struct wskbddev_attach_args a; 137 138 /* ohare based models have soft ejectable card slot. */ 139 if (OF_finddevice("/bandit/ohare") != -1) 140 pcmcia_soft_eject = 1; 141 142 sc->origaddr = aa_args->origaddr; 143 sc->adbaddr = aa_args->adbaddr; 144 sc->handler_id = aa_args->handler_id; 145 146 sc->sc_leds = (u_int8_t)0x00; /* initially off */ 147 148 adbinfo.siServiceRtPtr = (Ptr)kbd_adbcomplete; 149 adbinfo.siDataAreaAddr = (caddr_t)sc; 150 151 switch (sc->handler_id) { 152 case ADB_STDKBD: 153 printf("standard keyboard\n"); 154 break; 155 case ADB_ISOKBD: 156 printf("standard keyboard (ISO layout)\n"); 157 break; 158 case ADB_EXTKBD: 159 cmd = ADBTALK(sc->adbaddr, 1); 160 kbd_done = 161 (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0); 162 163 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */ 164 if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) { 165 printf("Mouseman (non-EMP) pseudo keyboard\n"); 166 adbinfo.siServiceRtPtr = (Ptr)0; 167 adbinfo.siDataAreaAddr = (Ptr)0; 168 } else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) { 169 printf("Trackman (non-EMP) pseudo keyboard\n"); 170 adbinfo.siServiceRtPtr = (Ptr)0; 171 adbinfo.siDataAreaAddr = (Ptr)0; 172 } else { 173 printf("extended keyboard\n"); 174 #ifdef notyet 175 blinkleds(sc); 176 #endif 177 } 178 break; 179 case ADB_EXTISOKBD: 180 printf("extended keyboard (ISO layout)\n"); 181 #ifdef notyet 182 blinkleds(sc); 183 #endif 184 break; 185 case ADB_KBDII: 186 printf("keyboard II\n"); 187 break; 188 case ADB_ISOKBDII: 189 printf("keyboard II (ISO layout)\n"); 190 break; 191 case ADB_PBKBD: 192 printf("PowerBook keyboard\n"); 193 break; 194 case ADB_PBISOKBD: 195 printf("PowerBook keyboard (ISO layout)\n"); 196 break; 197 case ADB_ADJKPD: 198 printf("adjustable keypad\n"); 199 break; 200 case ADB_ADJKBD: 201 printf("adjustable keyboard\n"); 202 break; 203 case ADB_ADJISOKBD: 204 printf("adjustable keyboard (ISO layout)\n"); 205 break; 206 case ADB_ADJJAPKBD: 207 printf("adjustable keyboard (Japanese layout)\n"); 208 break; 209 case ADB_PBEXTISOKBD: 210 printf("PowerBook extended keyboard (ISO layout)\n"); 211 break; 212 case ADB_PBEXTJAPKBD: 213 printf("PowerBook extended keyboard (Japanese layout)\n"); 214 break; 215 case ADB_JPKBDII: 216 printf("keyboard II (Japanese layout)\n"); 217 break; 218 case ADB_PBEXTKBD: 219 printf("PowerBook extended keyboard\n"); 220 break; 221 case ADB_DESIGNKBD: 222 printf("extended keyboard\n"); 223 #ifdef notyet 224 blinkleds(sc); 225 #endif 226 break; 227 case ADB_PBJPKBD: 228 printf("PowerBook keyboard (Japanese layout)\n"); 229 break; 230 case ADB_PBG3KBD: 231 printf("PowerBook G3 keyboard\n"); 232 break; 233 case ADB_PBG3JPKBD: 234 printf("PowerBook G3 keyboard (Japanese layout)\n"); 235 break; 236 default: 237 printf("mapped device (%d)\n", sc->handler_id); 238 break; 239 } 240 error = SetADBInfo(&adbinfo, sc->adbaddr); 241 #ifdef ADB_DEBUG 242 if (adb_debug) 243 printf("akbd: returned %d from SetADBInfo\n", error); 244 #endif 245 246 if (akbd_is_console && !akbd_console_attached) { 247 wskbd_cnattach(&akbd_consops, sc, &akbd_keymapdata); 248 akbd_console_attached = 1; 249 } 250 251 a.console = akbd_is_console; 252 a.keymap = &akbd_keymapdata; 253 a.accessops = &akbd_accessops; 254 a.accesscookie = sc; 255 256 sc->sc_wskbddev = config_found(self, &a, wskbddevprint); 257 } 258 259 260 /* 261 * Handle putting the keyboard data received from the ADB into 262 * an ADB event record. 263 */ 264 void 265 kbd_adbcomplete(buffer, data_area, adb_command) 266 caddr_t buffer; 267 caddr_t data_area; 268 int adb_command; 269 { 270 adb_event_t event; 271 struct akbd_softc *ksc; 272 int adbaddr; 273 #ifdef ADB_DEBUG 274 int i; 275 276 if (adb_debug) 277 printf("adb: transaction completion\n"); 278 #endif 279 280 adbaddr = ADB_CMDADDR(adb_command); 281 ksc = (struct akbd_softc *)data_area; 282 283 event.addr = adbaddr; 284 event.hand_id = ksc->handler_id; 285 event.def_addr = ksc->origaddr; 286 event.byte_count = buffer[0]; 287 memcpy(event.bytes, buffer + 1, event.byte_count); 288 289 #ifdef ADB_DEBUG 290 if (adb_debug) { 291 printf("akbd: from %d at %d (org %d) %d:", event.addr, 292 event.hand_id, event.def_addr, buffer[0]); 293 for (i = 1; i <= buffer[0]; i++) 294 printf(" %x", buffer[i]); 295 printf("\n"); 296 } 297 #endif 298 299 microtime(&event.timestamp); 300 301 kbd_processevent(&event, ksc); 302 } 303 304 /* 305 * Given a keyboard ADB event, record the keycodes and call the key 306 * repeat handler, optionally passing the event through the mouse 307 * button emulation handler first. 308 */ 309 static void 310 kbd_processevent(event, ksc) 311 adb_event_t *event; 312 struct akbd_softc *ksc; 313 { 314 adb_event_t new_event; 315 316 new_event = *event; 317 new_event.u.k.key = event->bytes[0]; 318 new_event.bytes[1] = 0xff; 319 kbd_intr(&new_event); 320 #if NAED > 0 321 aed_input(&new_event); 322 #endif 323 if (event->bytes[1] != 0xff) { 324 new_event.u.k.key = event->bytes[1]; 325 new_event.bytes[0] = event->bytes[1]; 326 new_event.bytes[1] = 0xff; 327 kbd_intr(&new_event); 328 #if NAED > 0 329 aed_input(&new_event); 330 #endif 331 } 332 333 } 334 335 #ifdef notyet 336 /* 337 * Get the actual hardware LED state and convert it to softc format. 338 */ 339 static u_char 340 getleds(addr) 341 int addr; 342 { 343 short cmd; 344 u_char buffer[9], leds; 345 346 leds = 0x00; /* all off */ 347 buffer[0] = 0; 348 349 /* talk R2 */ 350 cmd = ADBTALK(addr, 2); 351 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 && 352 buffer[0] > 0) 353 leds = ~(buffer[2]) & 0x07; 354 355 return (leds); 356 } 357 358 /* 359 * Set the keyboard LED's. 360 * 361 * Automatically translates from ioctl/softc format to the 362 * actual keyboard register format 363 */ 364 static int 365 setleds(ksc, leds) 366 struct akbd_softc *ksc; 367 u_char leds; 368 { 369 int addr; 370 short cmd; 371 u_char buffer[9]; 372 373 if ((leds & 0x07) == (ksc->sc_leds & 0x07)) 374 return (0); 375 376 addr = ksc->adbaddr; 377 buffer[0] = 0; 378 379 cmd = ADBTALK(addr, 2); 380 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) 381 return (EIO); 382 383 leds = ~leds & 0x07; 384 buffer[2] &= 0xf8; 385 buffer[2] |= leds; 386 387 cmd = ADBLISTEN(addr, 2); 388 adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); 389 390 cmd = ADBTALK(addr, 2); 391 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) 392 return (EIO); 393 394 ksc->sc_leds = ~((u_int8_t)buffer[2]) & 0x07; 395 396 if ((buffer[2] & 0xf8) != leds) 397 return (EIO); 398 else 399 return (0); 400 } 401 402 /* 403 * Toggle all of the LED's on and off, just for show. 404 */ 405 static void 406 blinkleds(ksc) 407 struct akbd_softc *ksc; 408 { 409 int addr, i; 410 u_char blinkleds, origleds; 411 412 addr = ksc->adbaddr; 413 origleds = getleds(addr); 414 blinkleds = LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK; 415 416 (void)setleds(ksc, blinkleds); 417 418 for (i = 0; i < 10000; i++) 419 delay(50); 420 421 /* make sure that we restore the LED settings */ 422 i = 10; 423 do { 424 (void)setleds(ksc, (u_char)0x00); 425 } while (setleds(ksc, (u_char)0x00) && (i-- > 0)); 426 427 return; 428 } 429 #endif 430 431 int 432 akbd_enable(v, on) 433 void *v; 434 int on; 435 { 436 return 0; 437 } 438 439 void 440 akbd_set_leds(v, on) 441 void *v; 442 int on; 443 { 444 } 445 446 int 447 akbd_ioctl(v, cmd, data, flag, p) 448 void *v; 449 u_long cmd; 450 caddr_t data; 451 int flag; 452 struct proc *p; 453 { 454 #ifdef WSDISPLAY_COMPAT_RAWKBD 455 struct akbd_softc *sc = (struct akbd_softc *) v; 456 #endif 457 458 switch (cmd) { 459 460 case WSKBDIO_GTYPE: 461 *(int *)data = WSKBD_TYPE_ADB; 462 return 0; 463 case WSKBDIO_SETLEDS: 464 return 0; 465 case WSKBDIO_GETLEDS: 466 *(int *)data = 0; 467 return 0; 468 #ifdef WSDISPLAY_COMPAT_RAWKBD 469 case WSKBDIO_SETMODE: 470 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 471 return 0; 472 #endif 473 } 474 /* kbdioctl(...); */ 475 476 return EPASSTHROUGH; 477 } 478 479 extern int adb_polling; 480 481 void 482 kbd_passup(sc,key) 483 struct akbd_softc *sc; 484 int key; 485 { 486 if (sc->sc_polling) { 487 if (sc->sc_npolledkeys < 488 (sizeof(sc->sc_polledkeys)/sizeof(unsigned char))) { 489 sc->sc_polledkeys[sc->sc_npolledkeys++] = key; 490 } 491 #ifdef ADB_DEBUG 492 else { 493 printf("akbd: dumping polled key 0x%02x\n",key); 494 } 495 #endif 496 #ifdef WSDISPLAY_COMPAT_RAWKBD 497 } else if (sc->sc_rawkbd) { 498 char cbuf[2]; 499 int s; 500 int j = 0; 501 int c = keyboard[ADBK_KEYVAL(key)][3]; 502 503 if (c == 0) /* XXX */ 504 return; 505 506 if (c & 0x80) 507 cbuf[j++] = 0xe0; 508 509 cbuf[j++] = (c & 0x7f) | (ADBK_PRESS(key)? 0 : 0x80); 510 511 s = spltty(); 512 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 513 splx(s); 514 #endif 515 } else { 516 int press, val; 517 int type; 518 519 press = ADBK_PRESS(key); 520 val = ADBK_KEYVAL(key); 521 522 type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 523 524 wskbd_input(sc->sc_wskbddev, type, val); 525 } 526 } 527 528 int 529 kbd_intr(arg) 530 void *arg; 531 { 532 adb_event_t *event = arg; 533 int key; 534 #ifdef CAPS_IS_CONTROL 535 static int shift; 536 #endif 537 538 struct akbd_softc *sc = akbd_cd.cd_devs[0]; 539 540 key = event->u.k.key; 541 542 #ifdef CAPS_IS_CONTROL 543 /* 544 * Caps lock is weird. The key sequence generated is: 545 * press: down(57) [57] (LED turns on) 546 * release: up(127) [255] 547 * press: up(127) [255] 548 * release: up(57) [185] (LED turns off) 549 */ 550 if ((key == 57) || (key == 185)) 551 shift = 0; 552 553 if (key == 255) { 554 if (shift == 0) { 555 key = 185; 556 shift = 1; 557 } else { 558 key = 57; 559 shift = 0; 560 } 561 } 562 #endif 563 564 switch (key) { 565 #ifndef CAPS_IS_CONTROL 566 case 57: /* Caps Lock pressed */ 567 case 185: /* Caps Lock released */ 568 key = ADBK_KEYDOWN(ADBK_KEYVAL(key)); 569 kbd_passup(sc,key); 570 key = ADBK_KEYUP(ADBK_KEYVAL(key)); 571 break; 572 #endif 573 case 245: 574 if (pcmcia_soft_eject) 575 pm_eject_pcmcia(0); 576 break; 577 case 244: 578 if (pcmcia_soft_eject) 579 pm_eject_pcmcia(1); 580 break; 581 } 582 583 kbd_passup(sc,key); 584 585 return 0; 586 } 587 588 int 589 akbd_cnattach() 590 { 591 592 akbd_is_console = 1; 593 return 0; 594 } 595 596 void 597 akbd_cngetc(v, type, data) 598 void *v; 599 u_int *type; 600 int *data; 601 { 602 int key, press, val; 603 int s; 604 struct akbd_softc *sc = v; 605 606 s = splhigh(); 607 608 KASSERT(sc->sc_polling); 609 KASSERT(adb_polling); 610 611 while (sc->sc_npolledkeys == 0) { 612 adb_intr(); 613 DELAY(10000); /* XXX */ 614 } 615 616 splx(s); 617 618 key = sc->sc_polledkeys[0]; 619 sc->sc_npolledkeys--; 620 memmove(sc->sc_polledkeys,sc->sc_polledkeys+1, 621 sc->sc_npolledkeys * sizeof(unsigned char)); 622 623 press = ADBK_PRESS(key); 624 val = ADBK_KEYVAL(key); 625 626 *data = val; 627 *type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 628 } 629 630 void 631 akbd_cnpollc(v, on) 632 void *v; 633 int on; 634 { 635 struct akbd_softc *sc = v; 636 sc->sc_polling = on; 637 if (!on) { 638 int i; 639 for(i=0;i<sc->sc_npolledkeys;i++) { 640 kbd_passup(sc,sc->sc_polledkeys[i]); 641 } 642 sc->sc_npolledkeys = 0; 643 } 644 adb_polling = on; 645 } 646