adb_kbd.c revision 1.3
1/* $NetBSD: adb_kbd.c,v 1.3 2007/02/20 01:27:25 macallan 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: adb_kbd.c,v 1.3 2007/02/20 01:27:25 macallan Exp $"); 35 36#include <sys/param.h> 37#include <sys/device.h> 38#include <sys/fcntl.h> 39#include <sys/poll.h> 40#include <sys/select.h> 41#include <sys/proc.h> 42#include <sys/systm.h> 43#include <sys/kernel.h> 44#include <sys/sysctl.h> 45 46#include <dev/wscons/wsconsio.h> 47#include <dev/wscons/wskbdvar.h> 48#include <dev/wscons/wsksymdef.h> 49#include <dev/wscons/wsksymvar.h> 50#include <dev/wscons/wsmousevar.h> 51 52#include <dev/sysmon/sysmonvar.h> 53#include <dev/sysmon/sysmon_taskq.h> 54 55#include <machine/autoconf.h> 56#include <machine/keyboard.h> 57#include <machine/adbsys.h> 58 59#include <dev/adb/adbvar.h> 60#include <dev/adb/adb_keymap.h> 61 62#include "adbdebug.h" 63 64struct adbkbd_softc { 65 struct device sc_dev; 66 struct adb_device *sc_adbdev; 67 struct adb_bus_accessops *sc_ops; 68 struct device *sc_wskbddev; 69 struct device *sc_wsmousedev; 70 struct sysmon_pswitch sc_sm_pbutton; 71 int sc_leds; 72 int sc_have_led_control; 73 int sc_msg_len; 74 int sc_event; 75 int sc_poll; 76 int sc_polled_chars; 77 int sc_trans[3]; 78 int sc_capslock; 79#ifdef WSDISPLAY_COMPAT_RAWKBD 80 int sc_rawkbd; 81#endif 82 uint8_t sc_buffer[16]; 83 uint8_t sc_pollbuf[16]; 84 uint8_t sc_us; 85}; 86 87/* 88 * Function declarations. 89 */ 90static int adbkbd_match(struct device *, struct cfdata *, void *); 91static void adbkbd_attach(struct device *, struct device *, void *); 92 93static void adbkbd_initleds(struct adbkbd_softc *); 94static void adbkbd_keys(struct adbkbd_softc *, uint8_t, uint8_t); 95static inline void adbkbd_key(struct adbkbd_softc *, uint8_t); 96static int adbkbd_wait(struct adbkbd_softc *, int); 97 98/* Driver definition. */ 99CFATTACH_DECL(adbkbd, sizeof(struct adbkbd_softc), 100 adbkbd_match, adbkbd_attach, NULL, NULL); 101 102extern struct cfdriver akbd_cd; 103 104static int adbkbd_enable(void *, int); 105static int adbkbd_ioctl(void *, u_long, caddr_t, int, struct lwp *); 106static void adbkbd_set_leds(void *, int); 107static void adbkbd_handler(void *, int, uint8_t *); 108 109struct wskbd_accessops adbkbd_accessops = { 110 adbkbd_enable, 111 adbkbd_set_leds, 112 adbkbd_ioctl, 113}; 114 115static void adbkbd_cngetc(void *, u_int *, int *); 116static void adbkbd_cnpollc(void *, int); 117 118struct wskbd_consops adbkbd_consops = { 119 adbkbd_cngetc, 120 adbkbd_cnpollc, 121}; 122 123struct wskbd_mapdata adbkbd_keymapdata = { 124 akbd_keydesctab, 125#ifdef AKBD_LAYOUT 126 AKBD_LAYOUT, 127#else 128 KB_US, 129#endif 130}; 131 132static int adbkms_enable(void *); 133static int adbkms_ioctl(void *, u_long, caddr_t, int, struct lwp *); 134static void adbkms_disable(void *); 135 136const struct wsmouse_accessops adbkms_accessops = { 137 adbkms_enable, 138 adbkms_ioctl, 139 adbkms_disable, 140}; 141 142static int adbkbd_sysctl_button(SYSCTLFN_ARGS); 143static void adbkbd_setup_sysctl(struct adbkbd_softc *); 144 145#ifdef ADBKBD_DEBUG 146#define DPRINTF printf 147#else 148#define DPRINTF while (0) printf 149#endif 150 151static int adbkbd_is_console = 0; 152static int adbkbd_console_attached = 0; 153 154static int 155adbkbd_match(parent, cf, aux) 156 struct device *parent; 157 struct cfdata *cf; 158 void *aux; 159{ 160 struct adb_attach_args *aaa = aux; 161 162 if (aaa->dev->original_addr == ADBADDR_KBD) 163 return 1; 164 else 165 return 0; 166} 167 168static void 169adbkbd_attach(struct device *parent, struct device *self, void *aux) 170{ 171 struct adbkbd_softc *sc = (struct adbkbd_softc *)self; 172 struct adb_attach_args *aaa = aux; 173 short cmd; 174 struct wskbddev_attach_args a; 175 struct wsmousedev_attach_args am; 176 177 sc->sc_ops = aaa->ops; 178 sc->sc_adbdev = aaa->dev; 179 sc->sc_adbdev->cookie = sc; 180 sc->sc_adbdev->handler = adbkbd_handler; 181 sc->sc_us = ADBTALK(sc->sc_adbdev->current_addr, 0); 182 183 sc->sc_leds = 0; /* initially off */ 184 sc->sc_have_led_control = 0; 185 sc->sc_msg_len = 0; 186 sc->sc_poll = 0; 187 sc->sc_capslock = 0; 188 sc->sc_trans[1] = 103; /* F11 */ 189 sc->sc_trans[2] = 111; /* F12 */ 190 191 printf(" addr %d ", sc->sc_adbdev->current_addr); 192 193 switch (sc->sc_adbdev->handler_id) { 194 case ADB_STDKBD: 195 printf("standard keyboard\n"); 196 break; 197 case ADB_ISOKBD: 198 printf("standard keyboard (ISO layout)\n"); 199 break; 200 case ADB_EXTKBD: 201 cmd = ADBTALK(sc->sc_adbdev->current_addr, 1); 202 sc->sc_msg_len = 0; 203 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL); 204 adbkbd_wait(sc, 10); 205 206 /* Ignore Logitech MouseMan/Trackman pseudo keyboard */ 207 /* XXX needs testing */ 208 if (sc->sc_buffer[2] == 0x9a && sc->sc_buffer[3] == 0x20) { 209 printf("Mouseman (non-EMP) pseudo keyboard\n"); 210 return; 211 } else if (sc->sc_buffer[2] == 0x9a && 212 sc->sc_buffer[3] == 0x21) { 213 printf("Trackman (non-EMP) pseudo keyboard\n"); 214 return; 215 } else { 216 printf("extended keyboard\n"); 217 adbkbd_initleds(sc); 218 } 219 break; 220 case ADB_EXTISOKBD: 221 printf("extended keyboard (ISO layout)\n"); 222 adbkbd_initleds(sc); 223 break; 224 case ADB_KBDII: 225 printf("keyboard II\n"); 226 break; 227 case ADB_ISOKBDII: 228 printf("keyboard II (ISO layout)\n"); 229 break; 230 case ADB_PBKBD: 231 printf("PowerBook keyboard\n"); 232 break; 233 case ADB_PBISOKBD: 234 printf("PowerBook keyboard (ISO layout)\n"); 235 break; 236 case ADB_ADJKPD: 237 printf("adjustable keypad\n"); 238 break; 239 case ADB_ADJKBD: 240 printf("adjustable keyboard\n"); 241 break; 242 case ADB_ADJISOKBD: 243 printf("adjustable keyboard (ISO layout)\n"); 244 break; 245 case ADB_ADJJAPKBD: 246 printf("adjustable keyboard (Japanese layout)\n"); 247 break; 248 case ADB_PBEXTISOKBD: 249 printf("PowerBook extended keyboard (ISO layout)\n"); 250 break; 251 case ADB_PBEXTJAPKBD: 252 printf("PowerBook extended keyboard (Japanese layout)\n"); 253 break; 254 case ADB_JPKBDII: 255 printf("keyboard II (Japanese layout)\n"); 256 break; 257 case ADB_PBEXTKBD: 258 printf("PowerBook extended keyboard\n"); 259 break; 260 case ADB_DESIGNKBD: 261 printf("extended keyboard\n"); 262 adbkbd_initleds(sc); 263 break; 264 case ADB_PBJPKBD: 265 printf("PowerBook keyboard (Japanese layout)\n"); 266 break; 267 case ADB_PBG3KBD: 268 printf("PowerBook G3 keyboard\n"); 269 break; 270 case ADB_PBG3JPKBD: 271 printf("PowerBook G3 keyboard (Japanese layout)\n"); 272 break; 273 case ADB_IBOOKKBD: 274 printf("iBook keyboard\n"); 275 break; 276 default: 277 printf("mapped device (%d)\n", sc->sc_adbdev->handler_id); 278 break; 279 } 280 281 if (adbkbd_is_console && (adbkbd_console_attached == 0)) { 282 wskbd_cnattach(&adbkbd_consops, sc, &adbkbd_keymapdata); 283 adbkbd_console_attached = 1; 284 a.console = 1; 285 } else { 286 a.console = 0; 287 } 288 a.keymap = &adbkbd_keymapdata; 289 a.accessops = &adbkbd_accessops; 290 a.accesscookie = sc; 291 292 sc->sc_wskbddev = config_found_ia(self, "wskbddev", &a, wskbddevprint); 293 294 /* attach the mouse device */ 295 am.accessops = &adbkms_accessops; 296 am.accesscookie = sc; 297 sc->sc_wsmousedev = config_found_ia(self, "wsmousedev", &am, wsmousedevprint); 298 299 if (sc->sc_wsmousedev != NULL) 300 adbkbd_setup_sysctl(sc); 301 302 /* finally register the power button */ 303 sysmon_task_queue_init(); 304 memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch)); 305 sc->sc_sm_pbutton.smpsw_name = sc->sc_dev.dv_xname; 306 sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER; 307 if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0) 308 printf("%s: unable to register power button with sysmon\n", 309 sc->sc_dev.dv_xname); 310} 311 312static void 313adbkbd_handler(void *cookie, int len, uint8_t *data) 314{ 315 struct adbkbd_softc *sc = cookie; 316 317#ifdef ADBKBD_DEBUG 318 int i; 319 printf("%s: %02x - ", sc->sc_dev.dv_xname, sc->sc_us); 320 for (i = 0; i < len; i++) { 321 printf(" %02x", data[i]); 322 } 323 printf("\n"); 324#endif 325 if (len >= 2) { 326 if (data[1] == sc->sc_us) { 327 adbkbd_keys(sc, data[2], data[3]); 328 return; 329 } else { 330 memcpy(sc->sc_buffer, data, len); 331 } 332 sc->sc_msg_len = len; 333 wakeup(&sc->sc_event); 334 } else { 335 DPRINTF("bogus message\n"); 336 } 337} 338 339static int 340adbkbd_wait(struct adbkbd_softc *sc, int timeout) 341{ 342 int cnt = 0; 343 344 if (sc->sc_poll) { 345 while (sc->sc_msg_len == 0) { 346 sc->sc_ops->poll(sc->sc_ops->cookie); 347 } 348 } else { 349 while ((sc->sc_msg_len == 0) && (cnt < timeout)) { 350 tsleep(&sc->sc_event, 0, "adbkbdio", hz); 351 cnt++; 352 } 353 } 354 return (sc->sc_msg_len > 0); 355} 356 357static void 358adbkbd_keys(struct adbkbd_softc *sc, uint8_t k1, uint8_t k2) 359{ 360 361 /* keyboard event processing */ 362 363 DPRINTF("[%02x %02x]", k1, k2); 364 365 if (((k1 == k2) && (k1 == 0x7f)) || (k1 == 0x7e)) { 366 367 /* power button, report to sysmon */ 368 sysmon_pswitch_event(&sc->sc_sm_pbutton, 369 ADBK_PRESS(k1) ? PSWITCH_EVENT_PRESSED : 370 PSWITCH_EVENT_RELEASED); 371 } else { 372 373 adbkbd_key(sc, k1); 374 if (k2 != 0xff) 375 adbkbd_key(sc, k2); 376 } 377} 378 379static inline void 380adbkbd_key(struct adbkbd_softc *sc, uint8_t k) 381{ 382 383 if (sc->sc_poll) { 384 if (sc->sc_polled_chars >= 16) { 385 printf("%s: polling buffer is full\n", 386 sc->sc_dev.dv_xname); 387 } 388 sc->sc_pollbuf[sc->sc_polled_chars] = k; 389 sc->sc_polled_chars++; 390 return; 391 } 392 393 /* translate some keys to mouse events */ 394 if (sc->sc_wsmousedev != NULL) { 395 if (ADBK_KEYVAL(k) == sc->sc_trans[1]) { 396 wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 2 : 0, 397 0, 0, 0, 0, 398 WSMOUSE_INPUT_DELTA); 399 return; 400 } 401 if (ADBK_KEYVAL(k) == sc->sc_trans[2]) { 402 wsmouse_input(sc->sc_wsmousedev, ADBK_PRESS(k) ? 4 : 0, 403 0, 0, 0, 0, 404 WSMOUSE_INPUT_DELTA); 405 return; 406 } 407 } 408#ifdef WSDISPLAY_COMPAT_RAWKBD 409 if (sc->sc_rawkbd) { 410 char cbuf[2]; 411 int s; 412 int j = 0; 413 int c = keyboard[ADBK_KEYVAL(k)][3] 414 415 if (k & 0x80) 416 cbuf[j++] = 0xe0; 417 418 cbuf[j++] = (c & 0x7f) | (ADBK_PRESS(k)? 0 : 0x80); 419 420 s = spltty(); 421 wskbd_rawinput(sc->sc_wskbddev, cbuf, j); 422 splx(s); 423 } else { 424#endif 425 426 if (ADBK_KEYVAL(k) == 0x39) { 427 /* caps lock - send up and down */ 428 if (ADBK_PRESS(k) != sc->sc_capslock) { 429 sc->sc_capslock = ADBK_PRESS(k); 430 wskbd_input(sc->sc_wskbddev, 431 WSCONS_EVENT_KEY_DOWN, 0x39); 432 wskbd_input(sc->sc_wskbddev, 433 WSCONS_EVENT_KEY_UP, 0x39); 434 } 435 } else { 436 /* normal event */ 437 int type; 438 439 type = ADBK_PRESS(k) ? 440 WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 441 wskbd_input(sc->sc_wskbddev, type, ADBK_KEYVAL(k)); 442 } 443#ifdef WSDISPLAY_COMPAT_RAWKBD 444 } 445#endif 446} 447 448/* 449 * Set the keyboard LED's. 450 * 451 * Automatically translates from ioctl/softc format to the 452 * actual keyboard register format 453 */ 454static void 455adbkbd_set_leds(void *cookie, int leds) 456{ 457 struct adbkbd_softc *sc = cookie; 458 int aleds; 459 short cmd; 460 uint8_t buffer[2]; 461 462 DPRINTF("adbkbd_set_leds: %02x\n", leds); 463 if ((leds & 0x07) == (sc->sc_leds & 0x07)) 464 return; 465 466 if (sc->sc_have_led_control) { 467 468 aleds = (~leds & 0x04) | 3; 469 if (leds & 1) 470 aleds &= ~2; 471 if (leds & 2) 472 aleds &= ~1; 473 474 buffer[0] = 0xff; 475 buffer[1] = aleds | 0xf8; 476 477 cmd = ADBLISTEN(sc->sc_adbdev->current_addr, 2); 478 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 2, buffer); 479 } 480 481 sc->sc_leds = leds & 7; 482} 483 484static void 485adbkbd_initleds(struct adbkbd_softc *sc) 486{ 487 short cmd; 488 489 /* talk R2 */ 490 cmd = ADBTALK(sc->sc_adbdev->current_addr, 2); 491 sc->sc_msg_len = 0; 492 sc->sc_ops->send(sc->sc_ops->cookie, sc->sc_poll, cmd, 0, NULL); 493 if (!adbkbd_wait(sc, 10)) { 494 printf("unable to read LED state\n"); 495 return; 496 } 497 sc->sc_have_led_control = 1; 498 DPRINTF("have LED control\n"); 499 return; 500} 501 502static int 503adbkbd_enable(void *v, int on) 504{ 505 return 0; 506} 507 508static int 509adbkbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct lwp *l) 510{ 511 struct adbkbd_softc *sc = (struct adbkbd_softc *) v; 512 513 switch (cmd) { 514 515 case WSKBDIO_GTYPE: 516 *(int *)data = WSKBD_TYPE_ADB; 517 return 0; 518 case WSKBDIO_SETLEDS: 519 adbkbd_set_leds(sc, *(int *)data); 520 return 0; 521 case WSKBDIO_GETLEDS: 522 *(int *)data = sc->sc_leds; 523 return 0; 524#ifdef WSDISPLAY_COMPAT_RAWKBD 525 case WSKBDIO_SETMODE: 526 sc->sc_rawkbd = *(int *)data == WSKBD_RAW; 527 return 0; 528#endif 529 } 530 531 return EPASSTHROUGH; 532} 533 534int 535adbkbd_cnattach() 536{ 537 538 adbkbd_is_console = 1; 539 return 0; 540} 541 542static void 543adbkbd_cngetc(void *v, u_int *type, int *data) 544{ 545 struct adbkbd_softc *sc = v; 546 int key, press, val; 547 int s; 548 549 s = splhigh(); 550 551 KASSERT(sc->sc_poll); 552 553 DPRINTF("polling..."); 554 while (sc->sc_polled_chars == 0) { 555 sc->sc_ops->poll(sc->sc_ops->cookie); 556 } 557 DPRINTF(" got one\n"); 558 splx(s); 559 560 key = sc->sc_pollbuf[0]; 561 sc->sc_polled_chars--; 562 memmove(sc->sc_pollbuf, sc->sc_pollbuf + 1, 563 sc->sc_polled_chars); 564 565 press = ADBK_PRESS(key); 566 val = ADBK_KEYVAL(key); 567 568 *data = val; 569 *type = press ? WSCONS_EVENT_KEY_DOWN : WSCONS_EVENT_KEY_UP; 570} 571 572static void 573adbkbd_cnpollc(void *v, int on) 574{ 575 struct adbkbd_softc *sc = v; 576 577 sc->sc_poll = on; 578 if (!on) { 579 int i; 580 581 /* feed the poll buffer's content to wskbd */ 582 for (i = 0; i < sc->sc_polled_chars; i++) { 583 adbkbd_key(sc, sc->sc_pollbuf[i]); 584 } 585 sc->sc_polled_chars = 0; 586 } 587} 588 589/* stuff for the pseudo mouse */ 590static int 591adbkms_enable(void *v) 592{ 593 return 0; 594} 595 596static int 597adbkms_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct lwp *l) 598{ 599 600 switch (cmd) { 601 case WSMOUSEIO_GTYPE: 602 *(u_int *)data = WSMOUSE_TYPE_PSEUDO; 603 break; 604 605 default: 606 return (EPASSTHROUGH); 607 } 608 return (0); 609} 610 611static void 612adbkms_disable(void *v) 613{ 614} 615 616static void 617adbkbd_setup_sysctl(struct adbkbd_softc *sc) 618{ 619 struct sysctlnode *node, *me; 620 int ret; 621 622 DPRINTF("%s: sysctl setup\n", sc->sc_dev.dv_xname); 623 ret = sysctl_createv(NULL, 0, NULL, (const struct sysctlnode **)&me, 624 CTLFLAG_READWRITE, 625 CTLTYPE_NODE, sc->sc_dev.dv_xname, NULL, 626 NULL, 0, NULL, 0, 627 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 628 629 ret = sysctl_createv(NULL, 0, NULL, 630 (const struct sysctlnode **)&node, 631 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE, 632 CTLTYPE_INT, "middle", "middle mouse button", adbkbd_sysctl_button, 633 1, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 634 node->sysctl_data = sc; 635 636 ret = sysctl_createv(NULL, 0, NULL, 637 (const struct sysctlnode **)&node, 638 CTLFLAG_READWRITE | CTLFLAG_OWNDESC | CTLFLAG_IMMEDIATE, 639 CTLTYPE_INT, "right", "right mouse button", adbkbd_sysctl_button, 640 2, NULL, 0, CTL_MACHDEP, me->sysctl_num, CTL_CREATE, CTL_EOL); 641 node->sysctl_data = sc; 642} 643 644static int 645adbkbd_sysctl_button(SYSCTLFN_ARGS) 646{ 647 struct sysctlnode node = *rnode; 648 struct adbkbd_softc *sc=(struct adbkbd_softc *)node.sysctl_data; 649 const int *np = newp; 650 int btn = node.sysctl_idata, reg; 651 652 DPRINTF("adbkbd_sysctl_button %d\n", btn); 653 node.sysctl_idata = sc->sc_trans[btn]; 654 reg = sc->sc_trans[btn]; 655 if (np) { 656 /* we're asked to write */ 657 node.sysctl_data = ® 658 if (sysctl_lookup(SYSCTLFN_CALL(&node)) == 0) { 659 660 sc->sc_trans[btn] = node.sysctl_idata; 661 return 0; 662 } 663 return EINVAL; 664 } else { 665 node.sysctl_size = 4; 666 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 667 } 668} 669 670SYSCTL_SETUP(sysctl_adbkbdtrans_setup, "adbkbd translator setup") 671{ 672 673 sysctl_createv(NULL, 0, NULL, NULL, 674 CTLFLAG_PERMANENT, 675 CTLTYPE_NODE, "machdep", NULL, 676 NULL, 0, NULL, 0, 677 CTL_MACHDEP, CTL_EOL); 678} 679