1 /* $NetBSD: akbd.c,v 1.31 2026/09/16 06:10:38 nat 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/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: akbd.c,v 1.31 2026/09/16 06:10:38 nat Exp $"); 35 36 #include "opt_adb.h" 37 38 #include <sys/param.h> 39 #include <sys/device.h> 40 #include <sys/fcntl.h> 41 #include <sys/poll.h> 42 #include <sys/select.h> 43 #include <sys/proc.h> 44 #include <sys/signalvar.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 48 #include "aed.h" 49 #include "wskbd.h" 50 51 #include <dev/wscons/wsconsio.h> 52 #include <dev/wscons/wskbdvar.h> 53 #include <dev/wscons/wsksymdef.h> 54 #include <dev/wscons/wsksymvar.h> 55 56 #include <machine/autoconf.h> 57 #include <machine/cpu.h> 58 #define KEYBOARD_ARRAY 59 #include <machine/keyboard.h> 60 #include <machine/viareg.h> 61 62 #include <mac68k/mac68k/macrom.h> 63 #include <mac68k/dev/adbvar.h> 64 #include <mac68k/dev/aedvar.h> 65 #include <mac68k/dev/akbdmap.h> 66 #include <mac68k/dev/akbdvar.h> 67 #include <mac68k/dev/amsvar.h> 68 69 /* 70 * Function declarations. 71 */ 72 static int akbdmatch(device_t, cfdata_t, void *); 73 static void akbdattach(device_t, device_t, void *); 74 static void kbd_processevent(adb_event_t *, struct akbd_softc *); 75 #ifdef notyet 76 static u_char getleds(int); 77 static int setleds(struct akbd_softc *, u_char); 78 static void blinkleds(struct akbd_softc *); 79 #endif 80 81 /* 82 * Local variables. 83 */ 84 85 /* Driver definition. */ 86 CFATTACH_DECL_NEW(akbd, sizeof(struct akbd_softc), 87 akbdmatch, akbdattach, NULL, NULL); 88 89 extern struct cfdriver akbd_cd; 90 91 int kbd_intr(adb_event_t *, struct akbd_softc *); 92 int akbd_enable(void *, int); 93 void akbd_set_leds(void *, int); 94 int akbd_ioctl(void *, u_long, void *, int, struct lwp *); 95 96 struct wskbd_accessops akbd_accessops = { 97 akbd_enable, 98 akbd_set_leds, 99 akbd_ioctl, 100 }; 101 102 void akbd_cngetc(void *, u_int *, int *); 103 void akbd_cnpollc(void *, int); 104 int akbd_cnattach(void); 105 106 struct wskbd_consops akbd_consops = { 107 akbd_cngetc, 108 akbd_cnpollc, 109 }; 110 111 struct wskbd_mapdata akbd_keymapdata = { 112 akbd_keydesctab, 113 KB_US, 114 }; 115 116 int leds; 117 static int akbd_is_console(void); 118 119 static int 120 akbdmatch(device_t parent, cfdata_t cf, void *aux) 121 { 122 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux; 123 124 if (aa_args->origaddr == ADBADDR_KBD) 125 return 1; 126 else 127 return 0; 128 } 129 130 static void 131 akbdattach(device_t parent, device_t self, void *aux) 132 { 133 ADBSetInfoBlock adbinfo; 134 struct akbd_softc *sc = device_private(self); 135 struct adb_attach_args *aa_args = (struct adb_attach_args *)aux; 136 int error __unused, kbd_done; 137 short cmd; 138 u_char buffer[9]; 139 #if NWSKBD > 0 140 struct wskbddev_attach_args a; 141 static int akbd_console_initted; 142 int wskbd_eligible; 143 144 wskbd_eligible = 1; 145 #endif 146 147 sc->origaddr = aa_args->origaddr; 148 sc->adbaddr = aa_args->adbaddr; 149 sc->handler_id = aa_args->handler_id; 150 151 sc->sc_leds = (u_int8_t)0x00; /* initially off */ 152 153 adbinfo.siServiceRtPtr = (Ptr)adb_kbd_asmcomplete; 154 adbinfo.siDataAreaAddr = (void *)sc; 155 156 switch (sc->handler_id) { 157 case ADB_STDKBD: 158 printf("standard keyboard\n"); 159 break; 160 case ADB_ISOKBD: 161 printf("standard keyboard (ISO layout)\n"); 162 break; 163 case ADB_EXTKBD: 164 cmd = ADBTALK(sc->adbaddr, 1); 165 kbd_done = 166 (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0); 167 168 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */ 169 if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x20) { 170 printf("Mouseman (non-EMP) pseudo keyboard\n"); 171 adbinfo.siServiceRtPtr = (Ptr)0; 172 adbinfo.siDataAreaAddr = (Ptr)0; 173 #if NWSKBD > 0 174 wskbd_eligible = 0; 175 #endif /* NWSKBD > 0 */ 176 } else if (kbd_done && buffer[1] == 0x9a && buffer[2] == 0x21) { 177 printf("Trackman (non-EMP) pseudo keyboard\n"); 178 adbinfo.siServiceRtPtr = (Ptr)0; 179 adbinfo.siDataAreaAddr = (Ptr)0; 180 #if NWSKBD > 0 181 wskbd_eligible = 0; 182 #endif /* NWSKBD > 0 */ 183 } else { 184 printf("extended keyboard\n"); 185 #ifdef notyet 186 blinkleds(sc); 187 #endif 188 } 189 break; 190 case ADB_EXTISOKBD: 191 printf("extended keyboard (ISO layout)\n"); 192 #ifdef notyet 193 blinkleds(sc); 194 #endif 195 break; 196 case ADB_KBDII: 197 printf("keyboard II\n"); 198 break; 199 case ADB_ISOKBDII: 200 printf("keyboard II (ISO layout)\n"); 201 break; 202 case ADB_PBKBD: 203 printf("PowerBook keyboard\n"); 204 break; 205 case ADB_PBISOKBD: 206 printf("PowerBook keyboard (ISO layout)\n"); 207 break; 208 case ADB_ADJKPD: 209 printf("adjustable keypad\n"); 210 #if NWSKBD > 0 211 wskbd_eligible = 0; 212 #endif /* NWSKBD > 0 */ 213 break; 214 case ADB_ADJKBD: 215 printf("adjustable keyboard\n"); 216 break; 217 case ADB_ADJISOKBD: 218 printf("adjustable keyboard (ISO layout)\n"); 219 break; 220 case ADB_ADJJAPKBD: 221 printf("adjustable keyboard (Japanese layout)\n"); 222 break; 223 case ADB_PBEXTISOKBD: 224 printf("PowerBook extended keyboard (ISO layout)\n"); 225 break; 226 case ADB_PBEXTJAPKBD: 227 printf("PowerBook extended keyboard (Japanese layout)\n"); 228 break; 229 case ADB_JPKBDII: 230 printf("keyboard II (Japanese layout)\n"); 231 break; 232 case ADB_PBEXTKBD: 233 printf("PowerBook extended keyboard\n"); 234 break; 235 case ADB_DESIGNKBD: 236 printf("extended keyboard\n"); 237 #ifdef notyet 238 blinkleds(sc); 239 #endif 240 break; 241 case ADB_PBJPKBD: 242 printf("PowerBook keyboard (Japanese layout)\n"); 243 break; 244 default: 245 printf("mapped device (%d)\n", sc->handler_id); 246 #if NWSKBD > 0 247 wskbd_eligible = 0; 248 #endif /* NWSKBD > 0 */ 249 break; 250 } 251 error = SetADBInfo(&adbinfo, sc->adbaddr); 252 #ifdef ADB_DEBUG 253 if (adb_debug) 254 printf("akbd: returned %d from SetADBInfo\n", error); 255 #endif 256 257 #if NWSKBD > 0 258 if (akbd_is_console() && wskbd_eligible) 259 a.console = (++akbd_console_initted == 1); 260 else 261 a.console = 0; 262 a.keymap = &akbd_keymapdata; 263 a.accessops = &akbd_accessops; 264 a.accesscookie = sc; 265 266 sc->sc_wskbddev = config_found(self, &a, wskbddevprint, CFARGS_NONE); 267 #endif 268 } 269 270 271 /* 272 * Handle putting the keyboard data received from the ADB into 273 * an ADB event record. 274 */ 275 void 276 kbd_adbcomplete(uint8_t *buffer, void *data_area, int adb_command) 277 { 278 adb_event_t event; 279 struct akbd_softc *ksc; 280 int adbaddr; 281 #ifdef ADB_DEBUG 282 int i; 283 284 if (adb_debug) 285 printf("adb: transaction completion\n"); 286 #endif 287 288 adbaddr = ADB_CMDADDR(adb_command); 289 ksc = (struct akbd_softc *)data_area; 290 291 event.addr = adbaddr; 292 event.hand_id = ksc->handler_id; 293 event.def_addr = ksc->origaddr; 294 event.byte_count = buffer[0]; 295 memcpy(event.bytes, buffer + 1, event.byte_count); 296 297 #ifdef ADB_DEBUG 298 if (adb_debug) { 299 printf("akbd: from %d at %d (org %d) %d:", event.addr, 300 event.hand_id, event.def_addr, buffer[0]); 301 for (i = 1; i <= buffer[0]; i++) 302 printf(" %x", buffer[i]); 303 printf("\n"); 304 } 305 #endif 306 307 microtime(&event.timestamp); 308 309 kbd_processevent(&event, ksc); 310 } 311 312 /* 313 * Given a keyboard ADB event, record the keycodes and call the key 314 * repeat handler, optionally passing the event through the mouse 315 * button emulation handler first. 316 */ 317 static void 318 kbd_processevent(adb_event_t *event, struct akbd_softc *ksc) 319 { 320 adb_event_t new_event; 321 322 new_event = *event; 323 new_event.u.k.key = event->bytes[0]; 324 new_event.bytes[1] = 0xff; 325 #if NAED > 0 326 int result; 327 328 if ((result = aed_input(&new_event)) != 0) 329 return; 330 331 if (adb_polling || !result) 332 #endif 333 #if NWSKBD > 0 334 if (ksc->sc_wskbddev != NULL) /* wskbd is attached? */ 335 kbd_intr(&new_event, ksc); 336 #else 337 /* do nothing */ ; 338 #endif 339 if (event->bytes[1] != 0xff) { 340 new_event.u.k.key = event->bytes[1]; 341 new_event.bytes[0] = event->bytes[1]; 342 new_event.bytes[1] = 0xff; 343 #if NAED > 0 344 if (adb_polling || !aed_input(&new_event)) 345 #endif 346 #if NWSKBD > 0 347 if (ksc->sc_wskbddev != NULL) /* wskbd is attached? */ 348 kbd_intr(&new_event, ksc); 349 #else 350 /* do nothing */ ; 351 #endif 352 } 353 354 } 355 356 #ifdef notyet 357 /* 358 * Get the actual hardware LED state and convert it to softc format. 359 */ 360 static u_char 361 getleds(int addr) 362 { 363 short cmd; 364 u_char buffer[9], leds; 365 366 leds = 0x00; /* all off */ 367 buffer[0] = 0; 368 369 cmd = ADBTALK(addr, 2); 370 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) == 0 && 371 buffer[0] > 0) 372 leds = ~(buffer[2]) & 0x07; 373 374 return (leds); 375 } 376 377 /* 378 * Set the keyboard LED's. 379 * 380 * Automatically translates from ioctl/softc format to the 381 * actual keyboard register format 382 */ 383 static int 384 setleds(struct akbd_softc *ksc, u_char leds) 385 { 386 int addr; 387 short cmd; 388 u_char buffer[9]; 389 390 if ((leds & 0x07) == (ksc->sc_leds & 0x07)) 391 return (0); 392 393 addr = ksc->adbaddr; 394 buffer[0] = 0; 395 396 cmd = ADBTALK(addr, 2); 397 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) 398 return (EIO); 399 400 leds = ~leds & 0x07; 401 buffer[2] &= 0xf8; 402 buffer[2] |= leds; 403 404 cmd = ADBLISTEN(addr, 2); 405 adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd); 406 407 /* talk R2 */ 408 cmd = ADBTALK(addr, 2); 409 if (adb_op_sync((Ptr)buffer, (Ptr)0, (Ptr)0, cmd) || buffer[0] == 0) 410 return (EIO); 411 412 ksc->sc_leds = ~((u_int8_t)buffer[2]) & 0x07; 413 414 if ((buffer[2] & 0xf8) != leds) 415 return (EIO); 416 else 417 return (0); 418 } 419 420 /* 421 * Toggle all of the LED's on and off, just for show. 422 */ 423 static void 424 blinkleds(struct akbd_softc *ksc) 425 { 426 int addr, i; 427 u_char blinkleds, origleds; 428 429 addr = ksc->adbaddr; 430 origleds = getleds(addr); 431 blinkleds = LED_NUMLOCK | LED_CAPSLOCK | LED_SCROLL_LOCK; 432 433 (void)setleds(ksc, blinkleds); 434 435 for (i = 0; i < 10000; i++) 436 delay(50); 437 438 /* make sure that we restore the LED settings */ 439 i = 10; 440 do { 441 (void)setleds(ksc, (u_char)origleds); 442 } while (setleds(ksc, (u_char)origleds) && (i-- > 0)); 443 444 return; 445 } 446 #endif 447 448 int 449 akbd_is_console(void) 450 { 451 extern struct mac68k_machine_S mac68k_machine; 452 453 return ((mac68k_machine.serial_console & 0x03) == 0); 454 } 455 456 int 457 akbd_enable(void *v, int on) 458 { 459 return 0; 460 } 461 462 void 463 akbd_set_leds(void *v, int on) 464 { 465 leds = on; 466 } 467 468 int 469 akbd_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l) 470 { 471 #ifdef WSDISPLAY_COMPAT_RAWKBD 472 struct akbd_softc *ksc = (struct akbd_softc *) v; 473 #endif 474 475 switch (cmd) { 476 477 case WSKBDIO_GTYPE: 478 *(int *)data = WSKBD_TYPE_ADB; 479 return 0; 480 case WSKBDIO_SETLEDS: 481 #ifdef notyet 482 setleds(ksc, *(int *)data); 483 #else 484 leds = *(int *)data; 485 #endif 486 return 0; 487 case WSKBDIO_GETLEDS: 488 #ifdef notyet 489 *(int *)data = getleds(ksc->adbaddr); 490 #else 491 *(int *)data = leds; 492 #endif 493 return 0; 494 case WSKBDIO_COMPLEXBELL: 495 #define d ((struct wskbd_bell_data *)data) 496 /* period: msec in, ticks out; volume is 0-100 */ 497 mac68k_ring_bell(d->pitch, d->period * hz / 1000, 498 d->volume > 100 ? 100 : d->volume); 499 #undef d 500 return (0); 501 #ifdef WSDISPLAY_COMPAT_RAWKBD 502 case WSKBDIO_SETMODE: 503 ksc->sc_rawkbd = *(int *)data == WSKBD_RAW; 504 return (0); 505 #endif 506 } 507 /* kbdioctl(...); */ 508 509 return EPASSTHROUGH; 510 } 511 512 static int polledkey; 513 extern int adb_polling; 514 515 int 516 kbd_intr(adb_event_t *event, struct akbd_softc *sc) 517 { 518 int key, press, val; 519 int type; 520 521 key = event->u.k.key; 522 press = ADBK_PRESS(key); 523 val = ADBK_KEYVAL(key); 524 525 type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 526 527 if (key == 185) { /* Caps Lock released */ 528 type = WSCONS_EVENT_KEY_DOWN; 529 wskbd_input(sc->sc_wskbddev, type, val); 530 type = WSCONS_EVENT_KEY_UP; 531 } 532 533 if (adb_polling) 534 polledkey = key; 535 #ifdef WSDISPLAY_COMPAT_RAWKBD 536 else if (sc->sc_rawkbd) { 537 char cbuf[2]; 538 int s; 539 int j = 0; 540 int c = keyboard[ADBK_KEYVAL(key)][3]; 541 542 if (c == 0) /* XXX */ 543 return (0); 544 545 if (c & 0x80) 546 cbuf[j++] = 0xe0; 547 548 cbuf[j++] = (c & 0x7f) | (ADBK_PRESS(key)? 0 : 0x80); 549 550 s = spltty(); 551 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 552 splx(s); 553 } 554 #endif 555 else 556 wskbd_input(sc->sc_wskbddev, type, val); 557 558 return 0; 559 } 560 561 int 562 akbd_cnattach(void) 563 { 564 wskbd_cnattach(&akbd_consops, NULL, &akbd_keymapdata); 565 566 return 0; 567 } 568 569 void 570 akbd_cngetc(void *v, u_int *type, int *data) 571 { 572 int intbits, key, press, val; 573 int s; 574 575 s = splhigh(); 576 577 polledkey = -1; 578 adb_polling = 1; 579 580 while (polledkey == -1) { 581 intbits = via_reg(VIA1, vIFR); 582 583 if (intbits & V1IF_ADBRDY) { 584 mrg_adbintr(); 585 via_reg(VIA1, vIFR) = V1IF_ADBRDY; 586 } 587 if (intbits & 0x10) { 588 mrg_pmintr(); 589 via_reg(VIA1, vIFR) = 0x10; 590 } 591 } 592 593 adb_polling = 0; 594 splx(s); 595 596 key = polledkey; 597 press = ADBK_PRESS(key); 598 val = ADBK_KEYVAL(key); 599 600 *data = val; 601 *type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 602 } 603 604 void 605 akbd_cnpollc(void *v, int on) 606 { 607 } 608