1 /* $OpenBSD: ukbd.c,v 1.84 2021/09/12 06:58:08 anton Exp $ */ 2 /* $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 2010 Miodrag Vallat. 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 /* 20 * Copyright (c) 1998 The NetBSD Foundation, Inc. 21 * All rights reserved. 22 * 23 * This code is derived from software contributed to The NetBSD Foundation 24 * by Lennart Augustsson (lennart@augustsson.net) at 25 * Carlstedt Research & Technology. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 36 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 37 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 38 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 39 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 40 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 41 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 44 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 46 * POSSIBILITY OF SUCH DAMAGE. 47 */ 48 49 /* 50 * HID spec: https://www.usb.org/sites/default/files/hid1_11.pdf 51 */ 52 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/timeout.h> 56 #include <sys/kernel.h> 57 #include <sys/device.h> 58 #include <sys/ioctl.h> 59 60 #include <machine/bus.h> 61 62 #include <dev/usb/usb.h> 63 #include <dev/usb/usbhid.h> 64 65 #include <dev/usb/usbdi.h> 66 #include <dev/usb/usbdivar.h> /* needs_reattach() */ 67 #include <dev/usb/usbdi_util.h> 68 #include <dev/usb/usbdevs.h> 69 #include <dev/usb/usb_quirks.h> 70 #include <dev/usb/uhidev.h> 71 #include <dev/usb/ukbdvar.h> 72 73 #include <dev/wscons/wsconsio.h> 74 #include <dev/wscons/wskbdvar.h> 75 #include <dev/wscons/wsksymdef.h> 76 #include <dev/wscons/wsksymvar.h> 77 78 #include <dev/hid/hidkbdsc.h> 79 80 #ifdef UKBD_DEBUG 81 #define DPRINTF(x) do { if (ukbddebug) printf x; } while (0) 82 #define DPRINTFN(n,x) do { if (ukbddebug>(n)) printf x; } while (0) 83 int ukbddebug = 0; 84 #else 85 #define DPRINTF(x) 86 #define DPRINTFN(n,x) 87 #endif 88 89 const kbd_t ukbd_countrylayout[1 + HCC_MAX] = { 90 (kbd_t)-1, 91 (kbd_t)-1, /* arabic */ 92 KB_BE, /* belgian */ 93 (kbd_t)-1, /* canadian bilingual */ 94 KB_CF, /* canadian french */ 95 (kbd_t)-1, /* czech */ 96 KB_DK, /* danish */ 97 (kbd_t)-1, /* finnish */ 98 KB_FR, /* french */ 99 KB_DE, /* german */ 100 (kbd_t)-1, /* greek */ 101 (kbd_t)-1, /* hebrew */ 102 KB_HU, /* hungary */ 103 (kbd_t)-1, /* international (iso) */ 104 KB_IT, /* italian */ 105 KB_JP, /* japanese (katakana) */ 106 (kbd_t)-1, /* korean */ 107 KB_LA, /* latin american */ 108 (kbd_t)-1, /* netherlands/dutch */ 109 KB_NO, /* norwegian */ 110 (kbd_t)-1, /* persian (farsi) */ 111 KB_PL, /* polish */ 112 KB_PT, /* portuguese */ 113 KB_RU, /* russian */ 114 (kbd_t)-1, /* slovakia */ 115 KB_ES, /* spanish */ 116 KB_SV, /* swedish */ 117 KB_SF, /* swiss french */ 118 KB_SG, /* swiss german */ 119 (kbd_t)-1, /* switzerland */ 120 (kbd_t)-1, /* taiwan */ 121 KB_TR, /* turkish Q */ 122 KB_UK, /* uk */ 123 KB_US, /* us */ 124 (kbd_t)-1, /* yugoslavia */ 125 (kbd_t)-1 /* turkish F */ 126 }; 127 128 struct ukbd_softc { 129 struct uhidev sc_hdev; 130 #define sc_ledsize sc_hdev.sc_osize 131 132 struct hidkbd sc_kbd; 133 int sc_spl; 134 struct hid_location sc_apple_fn; 135 void (*sc_munge)(void *, uint8_t *, u_int); 136 137 #ifdef DDB 138 struct timeout sc_ddb; /* for entering DDB */ 139 #endif 140 }; 141 142 void ukbd_cngetc(void *, u_int *, int *); 143 void ukbd_cnpollc(void *, int); 144 void ukbd_cnbell(void *, u_int, u_int, u_int); 145 void ukbd_debugger(void *); 146 147 const struct wskbd_consops ukbd_consops = { 148 ukbd_cngetc, 149 ukbd_cnpollc, 150 ukbd_cnbell, 151 #ifdef DDB 152 ukbd_debugger, 153 #endif 154 }; 155 156 void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len); 157 158 void ukbd_db_enter(void *); 159 int ukbd_enable(void *, int); 160 void ukbd_set_leds(void *, int); 161 int ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 162 163 const struct wskbd_accessops ukbd_accessops = { 164 ukbd_enable, 165 ukbd_set_leds, 166 ukbd_ioctl, 167 }; 168 169 int ukbd_match(struct device *, void *, void *); 170 void ukbd_attach(struct device *, struct device *, void *); 171 int ukbd_detach(struct device *, int); 172 173 struct cfdriver ukbd_cd = { 174 NULL, "ukbd", DV_DULL 175 }; 176 177 const struct cfattach ukbd_ca = { 178 sizeof(struct ukbd_softc), ukbd_match, ukbd_attach, ukbd_detach 179 }; 180 181 struct ukbd_translation { 182 uint8_t original; 183 uint8_t translation; 184 }; 185 186 #ifdef __loongson__ 187 void ukbd_gdium_munge(void *, uint8_t *, u_int); 188 #endif 189 void ukbd_apple_munge(void *, uint8_t *, u_int); 190 void ukbd_apple_mba_munge(void *, uint8_t *, u_int); 191 void ukbd_apple_iso_munge(void *, uint8_t *, u_int); 192 void ukbd_apple_iso_mba_munge(void *, uint8_t *, u_int); 193 void ukbd_apple_translate(void *, uint8_t *, u_int, 194 const struct ukbd_translation *, u_int); 195 uint8_t ukbd_translate(const struct ukbd_translation *, size_t, uint8_t); 196 197 int 198 ukbd_match(struct device *parent, void *match, void *aux) 199 { 200 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 201 int size; 202 void *desc; 203 204 uhidev_get_report_desc(uha->parent, &desc, &size); 205 if (!hid_is_collection(desc, size, uha->reportid, 206 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 207 return (UMATCH_NONE); 208 209 return (UMATCH_IFACECLASS); 210 } 211 212 void 213 ukbd_attach(struct device *parent, struct device *self, void *aux) 214 { 215 struct ukbd_softc *sc = (struct ukbd_softc *)self; 216 struct hidkbd *kbd = &sc->sc_kbd; 217 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 218 struct usb_hid_descriptor *hid; 219 u_int32_t quirks, qflags = 0; 220 int dlen, repid; 221 int console = 1; 222 void *desc; 223 kbd_t layout = (kbd_t)-1; 224 225 sc->sc_hdev.sc_intr = ukbd_intr; 226 sc->sc_hdev.sc_parent = uha->parent; 227 sc->sc_hdev.sc_udev = uha->uaa->device; 228 sc->sc_hdev.sc_report_id = uha->reportid; 229 230 usbd_set_idle(uha->parent->sc_udev, uha->parent->sc_ifaceno, 0, 0); 231 232 uhidev_get_report_desc(uha->parent, &desc, &dlen); 233 repid = uha->reportid; 234 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 235 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 236 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 237 238 /* 239 * Since the HID-Proxy is always detected before any 240 * real keyboard, do not let it grab the console. 241 */ 242 if (uha->uaa->vendor == USB_VENDOR_APPLE && 243 uha->uaa->product == USB_PRODUCT_APPLE_BLUETOOTH_HCI) 244 console = 0; 245 246 quirks = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags; 247 if (quirks & UQ_SPUR_BUT_UP) 248 qflags |= HIDKBD_SPUR_BUT_UP; 249 250 if (hidkbd_attach(self, kbd, console, qflags, repid, desc, dlen) != 0) 251 return; 252 253 if (uha->uaa->vendor == USB_VENDOR_APPLE) { 254 if (hid_locate(desc, dlen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY), 255 uha->reportid, hid_input, &sc->sc_apple_fn, &qflags)) { 256 if (qflags & HIO_VARIABLE) { 257 switch (uha->uaa->product) { 258 case USB_PRODUCT_APPLE_FOUNTAIN_ISO: 259 case USB_PRODUCT_APPLE_GEYSER_ISO: 260 case USB_PRODUCT_APPLE_GEYSER3_ISO: 261 case USB_PRODUCT_APPLE_WELLSPRING6_ISO: 262 case USB_PRODUCT_APPLE_WELLSPRING8_ISO: 263 sc->sc_munge = ukbd_apple_iso_munge; 264 break; 265 case USB_PRODUCT_APPLE_WELLSPRING_ISO: 266 case USB_PRODUCT_APPLE_WELLSPRING4_ISO: 267 case USB_PRODUCT_APPLE_WELLSPRING4A_ISO: 268 sc->sc_munge = ukbd_apple_iso_mba_munge; 269 break; 270 case USB_PRODUCT_APPLE_WELLSPRING_ANSI: 271 case USB_PRODUCT_APPLE_WELLSPRING_JIS: 272 case USB_PRODUCT_APPLE_WELLSPRING4_ANSI: 273 case USB_PRODUCT_APPLE_WELLSPRING4_JIS: 274 case USB_PRODUCT_APPLE_WELLSPRING4A_ANSI: 275 case USB_PRODUCT_APPLE_WELLSPRING4A_JIS: 276 sc->sc_munge = ukbd_apple_mba_munge; 277 break; 278 default: 279 sc->sc_munge = ukbd_apple_munge; 280 break; 281 } 282 } 283 } 284 } 285 286 if (uha->uaa->vendor == USB_VENDOR_TOPRE && 287 uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) { 288 /* ignore country code on purpose */ 289 } else { 290 usb_interface_descriptor_t *id; 291 292 id = usbd_get_interface_descriptor(uha->uaa->iface); 293 hid = usbd_get_hid_descriptor(uha->uaa->device, id); 294 295 if (hid->bCountryCode <= HCC_MAX) 296 layout = ukbd_countrylayout[hid->bCountryCode]; 297 #ifdef DIAGNOSTIC 298 if (hid->bCountryCode != 0) 299 printf(", country code %d", hid->bCountryCode); 300 #endif 301 } 302 if (layout == (kbd_t)-1) { 303 #ifdef UKBD_LAYOUT 304 layout = UKBD_LAYOUT; 305 #else 306 layout = KB_US | KB_DEFAULT; 307 #endif 308 } 309 310 printf("\n"); 311 312 #ifdef __loongson__ 313 if (uha->uaa->vendor == USB_VENDOR_CYPRESS && 314 uha->uaa->product == USB_PRODUCT_CYPRESS_LPRDK) 315 sc->sc_munge = ukbd_gdium_munge; 316 #endif 317 318 if (kbd->sc_console_keyboard) { 319 extern struct wskbd_mapdata ukbd_keymapdata; 320 321 DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc)); 322 ukbd_keymapdata.layout = layout; 323 wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); 324 ukbd_enable(sc, 1); 325 } 326 327 /* Flash the leds; no real purpose, just shows we're alive. */ 328 ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | 329 WSKBD_LED_CAPS | WSKBD_LED_COMPOSE); 330 usbd_delay_ms(sc->sc_hdev.sc_udev, 400); 331 ukbd_set_leds(sc, 0); 332 333 hidkbd_attach_wskbd(kbd, layout, &ukbd_accessops); 334 335 #ifdef DDB 336 timeout_set(&sc->sc_ddb, ukbd_db_enter, sc); 337 #endif 338 } 339 340 int 341 ukbd_detach(struct device *self, int flags) 342 { 343 struct ukbd_softc *sc = (struct ukbd_softc *)self; 344 struct hidkbd *kbd = &sc->sc_kbd; 345 int rv; 346 347 rv = hidkbd_detach(kbd, flags); 348 349 /* The console keyboard does not get a disable call, so check pipe. */ 350 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 351 uhidev_close(&sc->sc_hdev); 352 353 return (rv); 354 } 355 356 void 357 ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) 358 { 359 struct ukbd_softc *sc = (struct ukbd_softc *)addr; 360 struct hidkbd *kbd = &sc->sc_kbd; 361 362 if (kbd->sc_enabled != 0) { 363 if (sc->sc_munge != NULL) 364 (*sc->sc_munge)(sc, (uint8_t *)ibuf, len); 365 hidkbd_input(kbd, (uint8_t *)ibuf, len); 366 } 367 } 368 369 int 370 ukbd_enable(void *v, int on) 371 { 372 struct ukbd_softc *sc = v; 373 struct hidkbd *kbd = &sc->sc_kbd; 374 int rv; 375 376 if (on && usbd_is_dying(sc->sc_hdev.sc_udev)) 377 return EIO; 378 379 if ((rv = hidkbd_enable(kbd, on)) != 0) 380 return rv; 381 382 if (on) { 383 return uhidev_open(&sc->sc_hdev); 384 } else { 385 uhidev_close(&sc->sc_hdev); 386 return 0; 387 } 388 } 389 390 void 391 ukbd_set_leds(void *v, int leds) 392 { 393 struct ukbd_softc *sc = v; 394 struct hidkbd *kbd = &sc->sc_kbd; 395 u_int8_t res; 396 397 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 398 return; 399 400 if (sc->sc_ledsize && hidkbd_set_leds(kbd, leds, &res) != 0) 401 uhidev_set_report_async(sc->sc_hdev.sc_parent, 402 UHID_OUTPUT_REPORT, sc->sc_hdev.sc_report_id, &res, 1); 403 } 404 405 int 406 ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 407 { 408 struct ukbd_softc *sc = v; 409 struct hidkbd *kbd = &sc->sc_kbd; 410 int rc; 411 412 switch (cmd) { 413 case WSKBDIO_GTYPE: 414 *(int *)data = WSKBD_TYPE_USB; 415 return (0); 416 case WSKBDIO_SETLEDS: 417 ukbd_set_leds(v, *(int *)data); 418 return (0); 419 default: 420 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 421 if (rc != -1) 422 return rc; 423 else 424 return hidkbd_ioctl(kbd, cmd, data, flag, p); 425 } 426 } 427 428 /* Console interface. */ 429 void 430 ukbd_cngetc(void *v, u_int *type, int *data) 431 { 432 struct ukbd_softc *sc = v; 433 struct hidkbd *kbd = &sc->sc_kbd; 434 435 DPRINTFN(0,("ukbd_cngetc: enter\n")); 436 kbd->sc_polling = 1; 437 while (kbd->sc_npollchar <= 0) 438 usbd_dopoll(sc->sc_hdev.sc_udev); 439 kbd->sc_polling = 0; 440 hidkbd_cngetc(kbd, type, data); 441 DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", *data)); 442 } 443 444 void 445 ukbd_cnpollc(void *v, int on) 446 { 447 struct ukbd_softc *sc = v; 448 449 DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); 450 451 if (on) 452 sc->sc_spl = splusb(); 453 else 454 splx(sc->sc_spl); 455 usbd_set_polling(sc->sc_hdev.sc_udev, on); 456 } 457 458 void 459 ukbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 460 { 461 hidkbd_bell(pitch, period, volume, 1); 462 } 463 464 #ifdef DDB 465 void 466 ukbd_debugger(void *v) 467 { 468 struct ukbd_softc *sc = v; 469 470 /* 471 * For the console keyboard we can't deliver CTL-ALT-ESC 472 * from the interrupt routine. Doing so would start 473 * polling from inside the interrupt routine and that 474 * loses bigtime. 475 */ 476 timeout_add(&sc->sc_ddb, 1); 477 } 478 479 void 480 ukbd_db_enter(void *xsc) 481 { 482 db_enter(); 483 } 484 #endif 485 486 int 487 ukbd_cnattach(void) 488 { 489 struct ukbd_softc *sc; 490 int i; 491 492 /* 493 * XXX USB requires too many parts of the kernel to be running 494 * XXX in order to work, so we can't do much for the console 495 * XXX keyboard until autconfiguration has run its course. 496 */ 497 hidkbd_is_console = 1; 498 499 if (!cold) { 500 /* 501 * When switching console dynamically force all USB keyboards 502 * to re-attach and possibly became the 'console' keyboard. 503 */ 504 for (i = 0; i < ukbd_cd.cd_ndevs; i++) { 505 if ((sc = ukbd_cd.cd_devs[i]) != NULL) { 506 usb_needs_reattach(sc->sc_hdev.sc_udev); 507 break; 508 } 509 } 510 } 511 512 return (0); 513 } 514 515 uint8_t 516 ukbd_translate(const struct ukbd_translation *table, size_t tsize, 517 uint8_t keycode) 518 { 519 for (; tsize != 0; table++, tsize--) 520 if (table->original == keycode) 521 return table->translation; 522 return 0; 523 } 524 525 void 526 ukbd_apple_translate(void *vsc, uint8_t *ibuf, u_int ilen, 527 const struct ukbd_translation* trans, u_int tlen) 528 { 529 struct ukbd_softc *sc = vsc; 530 struct hidkbd *kbd = &sc->sc_kbd; 531 uint8_t *pos, *spos, *epos, xlat; 532 533 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 534 epos = spos + kbd->sc_nkeycode; 535 536 for (pos = spos; pos != epos; pos++) { 537 xlat = ukbd_translate(trans, tlen, *pos); 538 if (xlat != 0) 539 *pos = xlat; 540 } 541 } 542 543 void 544 ukbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen) 545 { 546 struct ukbd_softc *sc = vsc; 547 548 static const struct ukbd_translation apple_fn_trans[] = { 549 { 40, 73 }, /* return -> insert */ 550 { 42, 76 }, /* backspace -> delete */ 551 #ifdef notyet 552 { 58, 0 }, /* F1 -> screen brightness down */ 553 { 59, 0 }, /* F2 -> screen brightness up */ 554 { 60, 0 }, /* F3 */ 555 { 61, 0 }, /* F4 */ 556 { 62, 0 }, /* F5 -> keyboard backlight down */ 557 { 63, 0 }, /* F6 -> keyboard backlight up */ 558 { 64, 0 }, /* F7 -> audio back */ 559 { 65, 0 }, /* F8 -> audio pause/play */ 560 { 66, 0 }, /* F9 -> audio next */ 561 #endif 562 #ifdef __macppc__ 563 { 58, 233 }, /* F1 -> screen brightness down */ 564 { 59, 232 }, /* F2 -> screen brightness up */ 565 { 60, 127 }, /* F3 -> audio mute */ 566 { 61, 129 }, /* F4 -> audio lower */ 567 { 62, 128 }, /* F5 -> audio raise */ 568 #else 569 { 67, 127 }, /* F10 -> audio mute */ 570 { 68, 129 }, /* F11 -> audio lower */ 571 { 69, 128 }, /* F12 -> audio raise */ 572 #endif 573 { 79, 77 }, /* right -> end */ 574 { 80, 74 }, /* left -> home */ 575 { 81, 78 }, /* down -> page down */ 576 { 82, 75 } /* up -> page up */ 577 }; 578 579 if (!hid_get_data(ibuf, ilen, &sc->sc_apple_fn)) 580 return; 581 582 ukbd_apple_translate(vsc, ibuf, ilen, apple_fn_trans, 583 nitems(apple_fn_trans)); 584 } 585 586 void 587 ukbd_apple_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen) 588 { 589 struct ukbd_softc *sc = vsc; 590 591 static const struct ukbd_translation apple_fn_trans[] = { 592 { 40, 73 }, /* return -> insert */ 593 { 42, 76 }, /* backspace -> delete */ 594 #ifdef notyet 595 { 58, 0 }, /* F1 -> screen brightness down */ 596 { 59, 0 }, /* F2 -> screen brightness up */ 597 { 60, 0 }, /* F3 */ 598 { 61, 0 }, /* F4 */ 599 { 62, 0 }, /* F5 */ 600 { 63, 0 }, /* F6 -> audio back */ 601 { 64, 0 }, /* F7 -> audio pause/play */ 602 { 65, 0 }, /* F8 -> audio next */ 603 #endif 604 { 66, 127 }, /* F9 -> audio mute */ 605 { 67, 129 }, /* F10 -> audio lower */ 606 { 68, 128 }, /* F11 -> audio raise */ 607 #ifdef notyet 608 { 69, 0 }, /* F12 -> eject */ 609 #endif 610 { 79, 77 }, /* right -> end */ 611 { 80, 74 }, /* left -> home */ 612 { 81, 78 }, /* down -> page down */ 613 { 82, 75 } /* up -> page up */ 614 }; 615 616 if (!hid_get_data(ibuf, ilen, &sc->sc_apple_fn)) 617 return; 618 619 ukbd_apple_translate(vsc, ibuf, ilen, apple_fn_trans, 620 nitems(apple_fn_trans)); 621 } 622 623 void 624 ukbd_apple_iso_munge(void *vsc, uint8_t *ibuf, u_int ilen) 625 { 626 static const struct ukbd_translation apple_iso_trans[] = { 627 { 53, 100 }, /* less -> grave */ 628 { 100, 53 }, 629 }; 630 631 ukbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans, 632 nitems(apple_iso_trans)); 633 ukbd_apple_munge(vsc, ibuf, ilen); 634 } 635 636 void 637 ukbd_apple_iso_mba_munge(void *vsc, uint8_t *ibuf, u_int ilen) 638 { 639 static const struct ukbd_translation apple_iso_trans[] = { 640 { 53, 100 }, /* less -> grave */ 641 { 100, 53 }, 642 }; 643 644 ukbd_apple_translate(vsc, ibuf, ilen, apple_iso_trans, 645 nitems(apple_iso_trans)); 646 ukbd_apple_mba_munge(vsc, ibuf, ilen); 647 } 648 649 #ifdef __loongson__ 650 /* 651 * Software Fn- translation for Gdium Liberty keyboard. 652 */ 653 #define GDIUM_FN_CODE 0x82 654 void 655 ukbd_gdium_munge(void *vsc, uint8_t *ibuf, u_int ilen) 656 { 657 struct ukbd_softc *sc = vsc; 658 struct hidkbd *kbd = &sc->sc_kbd; 659 uint8_t *pos, *spos, *epos, xlat; 660 int fn; 661 662 static const struct ukbd_translation gdium_fn_trans[] = { 663 #ifdef notyet 664 { 58, 0 }, /* F1 -> toggle camera */ 665 { 59, 0 }, /* F2 -> toggle wireless */ 666 #endif 667 { 60, 127 }, /* F3 -> audio mute */ 668 { 61, 128 }, /* F4 -> audio raise */ 669 { 62, 129 }, /* F5 -> audio lower */ 670 #ifdef notyet 671 { 63, 0 }, /* F6 -> toggle ext. video */ 672 { 64, 0 }, /* F7 -> toggle mouse */ 673 { 65, 0 }, /* F8 -> brightness up */ 674 { 66, 0 }, /* F9 -> brightness down */ 675 { 67, 0 }, /* F10 -> suspend */ 676 { 68, 0 }, /* F11 -> user1 */ 677 { 69, 0 }, /* F12 -> user2 */ 678 { 70, 0 }, /* print screen -> sysrq */ 679 #endif 680 { 76, 71 }, /* delete -> scroll lock */ 681 { 81, 78 }, /* down -> page down */ 682 { 82, 75 } /* up -> page up */ 683 }; 684 685 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 686 epos = spos + kbd->sc_nkeycode; 687 688 /* 689 * Check for Fn key being down and remove it from the report. 690 */ 691 692 fn = 0; 693 for (pos = spos; pos != epos; pos++) 694 if (*pos == GDIUM_FN_CODE) { 695 fn = 1; 696 *pos = 0; 697 break; 698 } 699 700 /* 701 * Rewrite keycodes on the fly to perform Fn-key translation. 702 * Keycodes without a translation are passed unaffected. 703 */ 704 705 if (fn != 0) 706 for (pos = spos; pos != epos; pos++) { 707 xlat = ukbd_translate(gdium_fn_trans, 708 nitems(gdium_fn_trans), *pos); 709 if (xlat != 0) 710 *pos = xlat; 711 } 712 713 } 714 #endif 715