1 /* $OpenBSD: ukbd.c,v 1.64 2014/04/15 09:14:27 mpi 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: http://www.usb.org/developers/devclass_docs/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 <dev/usb/usb.h> 61 #include <dev/usb/usbhid.h> 62 63 #include <dev/usb/usbdi.h> 64 #include <dev/usb/usbdi_util.h> 65 #include <dev/usb/usbdevs.h> 66 #include <dev/usb/usb_quirks.h> 67 #include <dev/usb/uhidev.h> 68 #include <dev/usb/hid.h> 69 #include <dev/usb/ukbdvar.h> 70 71 #include <dev/wscons/wsconsio.h> 72 #include <dev/wscons/wskbdvar.h> 73 #include <dev/wscons/wsksymdef.h> 74 #include <dev/wscons/wsksymvar.h> 75 76 #include <dev/usb/hidkbdsc.h> 77 #include <dev/usb/hidkbdvar.h> 78 79 #ifdef UKBD_DEBUG 80 #define DPRINTF(x) do { if (ukbddebug) printf x; } while (0) 81 #define DPRINTFN(n,x) do { if (ukbddebug>(n)) printf x; } while (0) 82 int ukbddebug = 0; 83 #else 84 #define DPRINTF(x) 85 #define DPRINTFN(n,x) 86 #endif 87 88 const kbd_t ukbd_countrylayout[1 + HCC_MAX] = { 89 (kbd_t)-1, 90 (kbd_t)-1, /* arabic */ 91 KB_BE, /* belgian */ 92 (kbd_t)-1, /* canadian bilingual */ 93 KB_CF, /* canadian french */ 94 (kbd_t)-1, /* czech */ 95 KB_DK, /* danish */ 96 (kbd_t)-1, /* finnish */ 97 KB_FR, /* french */ 98 KB_DE, /* german */ 99 (kbd_t)-1, /* greek */ 100 (kbd_t)-1, /* hebrew */ 101 KB_HU, /* hungary */ 102 (kbd_t)-1, /* international (iso) */ 103 KB_IT, /* italian */ 104 KB_JP, /* japanese (katakana) */ 105 (kbd_t)-1, /* korean */ 106 KB_LA, /* latin american */ 107 (kbd_t)-1, /* netherlands/dutch */ 108 KB_NO, /* norwegian */ 109 (kbd_t)-1, /* persian (farsi) */ 110 KB_PL, /* polish */ 111 KB_PT, /* portuguese */ 112 KB_RU, /* russian */ 113 (kbd_t)-1, /* slovakia */ 114 KB_ES, /* spanish */ 115 KB_SV, /* swedish */ 116 KB_SF, /* swiss french */ 117 KB_SG, /* swiss german */ 118 (kbd_t)-1, /* switzerland */ 119 (kbd_t)-1, /* taiwan */ 120 KB_TR, /* turkish Q */ 121 KB_UK, /* uk */ 122 KB_US, /* us */ 123 (kbd_t)-1, /* yugoslavia */ 124 (kbd_t)-1 /* turkish F */ 125 }; 126 127 struct ukbd_softc { 128 struct uhidev sc_hdev; 129 #define sc_ledsize sc_hdev.sc_osize 130 131 struct hidkbd sc_kbd; 132 133 int sc_spl; 134 135 struct hid_location sc_apple_fn; 136 137 void (*sc_munge)(void *, uint8_t *, u_int); 138 }; 139 140 void ukbd_cngetc(void *, u_int *, int *); 141 void ukbd_cnpollc(void *, int); 142 void ukbd_cnbell(void *, u_int, u_int, u_int); 143 144 const struct wskbd_consops ukbd_consops = { 145 ukbd_cngetc, 146 ukbd_cnpollc, 147 ukbd_cnbell, 148 }; 149 150 void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len); 151 152 int ukbd_enable(void *, int); 153 void ukbd_set_leds(void *, int); 154 int ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 155 156 const struct wskbd_accessops ukbd_accessops = { 157 ukbd_enable, 158 ukbd_set_leds, 159 ukbd_ioctl, 160 }; 161 162 int ukbd_match(struct device *, void *, void *); 163 void ukbd_attach(struct device *, struct device *, void *); 164 int ukbd_detach(struct device *, int); 165 int ukbd_activate(struct device *, int); 166 167 struct cfdriver ukbd_cd = { 168 NULL, "ukbd", DV_DULL 169 }; 170 171 const struct cfattach ukbd_ca = { 172 sizeof(struct ukbd_softc), 173 ukbd_match, 174 ukbd_attach, 175 ukbd_detach, 176 ukbd_activate, 177 }; 178 179 struct ukbd_translation { 180 uint8_t original; 181 uint8_t translation; 182 }; 183 184 #ifdef __loongson__ 185 void ukbd_gdium_munge(void *, uint8_t *, u_int); 186 #endif 187 void ukbd_apple_munge(void *, uint8_t *, u_int); 188 void ukbd_apple_iso_munge(void *, uint8_t *, u_int); 189 uint8_t ukbd_translate(const struct ukbd_translation *, size_t, uint8_t); 190 191 int 192 ukbd_match(struct device *parent, void *match, void *aux) 193 { 194 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 195 int size; 196 void *desc; 197 198 uhidev_get_report_desc(uha->parent, &desc, &size); 199 if (!hid_is_collection(desc, size, uha->reportid, 200 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 201 return (UMATCH_NONE); 202 203 return (UMATCH_IFACECLASS); 204 } 205 206 void 207 ukbd_attach(struct device *parent, struct device *self, void *aux) 208 { 209 struct ukbd_softc *sc = (struct ukbd_softc *)self; 210 struct hidkbd *kbd = &sc->sc_kbd; 211 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)aux; 212 struct usb_hid_descriptor *hid; 213 u_int32_t qflags; 214 int dlen, repid; 215 void *desc; 216 kbd_t layout = (kbd_t)-1; 217 218 sc->sc_hdev.sc_intr = ukbd_intr; 219 sc->sc_hdev.sc_parent = uha->parent; 220 sc->sc_hdev.sc_udev = uha->uaa->device; 221 sc->sc_hdev.sc_report_id = uha->reportid; 222 223 uhidev_get_report_desc(uha->parent, &desc, &dlen); 224 repid = uha->reportid; 225 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 226 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 227 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 228 229 qflags = usbd_get_quirks(sc->sc_hdev.sc_udev)->uq_flags; 230 if (hidkbd_attach(self, kbd, 1, qflags, repid, desc, dlen) != 0) 231 return; 232 233 if (uha->uaa->vendor == USB_VENDOR_APPLE) { 234 int iso = 0; 235 236 if ((uha->uaa->product == USB_PRODUCT_APPLE_FOUNTAIN_ISO) || 237 (uha->uaa->product == USB_PRODUCT_APPLE_GEYSER_ISO)) 238 iso = 1; 239 240 if (hid_locate(desc, dlen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY), 241 uha->reportid, hid_input, &sc->sc_apple_fn, &qflags)) { 242 if (qflags & HIO_VARIABLE) { 243 if (iso) 244 sc->sc_munge = ukbd_apple_iso_munge; 245 else 246 sc->sc_munge = ukbd_apple_munge; 247 } 248 } 249 } 250 251 if (uha->uaa->vendor == USB_VENDOR_TOPRE && 252 uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) { 253 /* ignore country code on purpose */ 254 } else { 255 hid = usbd_get_hid_descriptor(uha->uaa->iface); 256 257 if (hid->bCountryCode <= HCC_MAX) 258 layout = ukbd_countrylayout[hid->bCountryCode]; 259 #ifdef DIAGNOSTIC 260 if (hid->bCountryCode != 0) 261 printf(", country code %d", hid->bCountryCode); 262 #endif 263 } 264 if (layout == (kbd_t)-1) { 265 #ifdef UKBD_LAYOUT 266 layout = UKBD_LAYOUT; 267 #else 268 layout = KB_US | KB_DEFAULT; 269 #endif 270 } 271 272 printf("\n"); 273 274 #ifdef __loongson__ 275 if (uha->uaa->vendor == USB_VENDOR_CYPRESS && 276 uha->uaa->product == USB_PRODUCT_CYPRESS_LPRDK) 277 sc->sc_munge = ukbd_gdium_munge; 278 #endif 279 280 if (kbd->sc_console_keyboard) { 281 extern struct wskbd_mapdata ukbd_keymapdata; 282 283 DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc)); 284 ukbd_keymapdata.layout = layout; 285 wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); 286 ukbd_enable(sc, 1); 287 } 288 289 /* Flash the leds; no real purpose, just shows we're alive. */ 290 ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | 291 WSKBD_LED_CAPS | WSKBD_LED_COMPOSE); 292 usbd_delay_ms(sc->sc_hdev.sc_udev, 400); 293 ukbd_set_leds(sc, 0); 294 295 hidkbd_attach_wskbd(kbd, layout, &ukbd_accessops); 296 } 297 298 int 299 ukbd_activate(struct device *self, int act) 300 { 301 struct ukbd_softc *sc = (struct ukbd_softc *)self; 302 struct hidkbd *kbd = &sc->sc_kbd; 303 int rv = 0; 304 305 switch (act) { 306 case DVACT_DEACTIVATE: 307 if (kbd->sc_wskbddev != NULL) 308 rv = config_deactivate(kbd->sc_wskbddev); 309 break; 310 } 311 return (rv); 312 } 313 314 int 315 ukbd_detach(struct device *self, int flags) 316 { 317 struct ukbd_softc *sc = (struct ukbd_softc *)self; 318 struct hidkbd *kbd = &sc->sc_kbd; 319 int rv; 320 321 rv = hidkbd_detach(kbd, flags); 322 323 /* The console keyboard does not get a disable call, so check pipe. */ 324 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 325 uhidev_close(&sc->sc_hdev); 326 327 return (rv); 328 } 329 330 void 331 ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) 332 { 333 struct ukbd_softc *sc = (struct ukbd_softc *)addr; 334 struct hidkbd *kbd = &sc->sc_kbd; 335 336 if (kbd->sc_enabled != 0) { 337 if (sc->sc_munge != NULL) 338 (*sc->sc_munge)(sc, (uint8_t *)ibuf, len); 339 hidkbd_input(kbd, (uint8_t *)ibuf, len); 340 } 341 } 342 343 int 344 ukbd_enable(void *v, int on) 345 { 346 struct ukbd_softc *sc = v; 347 struct hidkbd *kbd = &sc->sc_kbd; 348 int rv; 349 350 if (on && usbd_is_dying(sc->sc_hdev.sc_udev)) 351 return EIO; 352 353 if ((rv = hidkbd_enable(kbd, on)) != 0) 354 return rv; 355 356 if (on) { 357 return uhidev_open(&sc->sc_hdev); 358 } else { 359 uhidev_close(&sc->sc_hdev); 360 return 0; 361 } 362 } 363 364 void 365 ukbd_set_leds(void *v, int leds) 366 { 367 struct ukbd_softc *sc = v; 368 struct hidkbd *kbd = &sc->sc_kbd; 369 u_int8_t res; 370 371 if (usbd_is_dying(sc->sc_hdev.sc_udev)) 372 return; 373 374 if (sc->sc_ledsize && hidkbd_set_leds(kbd, leds, &res) != 0) 375 uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, 376 sc->sc_hdev.sc_report_id, &res, 1); 377 } 378 379 int 380 ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 381 { 382 struct ukbd_softc *sc = v; 383 struct hidkbd *kbd = &sc->sc_kbd; 384 int rc; 385 386 switch (cmd) { 387 case WSKBDIO_GTYPE: 388 *(int *)data = WSKBD_TYPE_USB; 389 return (0); 390 case WSKBDIO_SETLEDS: 391 ukbd_set_leds(v, *(int *)data); 392 return (0); 393 default: 394 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 395 if (rc != -1) 396 return rc; 397 else 398 return hidkbd_ioctl(kbd, cmd, data, flag, p); 399 } 400 } 401 402 /* Console interface. */ 403 void 404 ukbd_cngetc(void *v, u_int *type, int *data) 405 { 406 struct ukbd_softc *sc = v; 407 struct hidkbd *kbd = &sc->sc_kbd; 408 409 DPRINTFN(0,("ukbd_cngetc: enter\n")); 410 kbd->sc_polling = 1; 411 while (kbd->sc_npollchar <= 0) 412 usbd_dopoll(sc->sc_hdev.sc_udev); 413 kbd->sc_polling = 0; 414 hidkbd_cngetc(kbd, type, data); 415 DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", *data)); 416 } 417 418 void 419 ukbd_cnpollc(void *v, int on) 420 { 421 struct ukbd_softc *sc = v; 422 423 DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); 424 425 if (on) 426 sc->sc_spl = splusb(); 427 else 428 splx(sc->sc_spl); 429 usbd_set_polling(sc->sc_hdev.sc_udev, on); 430 } 431 432 void 433 ukbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 434 { 435 hidkbd_bell(pitch, period, volume, 1); 436 } 437 438 int 439 ukbd_cnattach(void) 440 { 441 442 /* 443 * XXX USB requires too many parts of the kernel to be running 444 * XXX in order to work, so we can't do much for the console 445 * XXX keyboard until autconfiguration has run its course. 446 */ 447 hidkbd_is_console = 1; 448 return (0); 449 } 450 451 uint8_t 452 ukbd_translate(const struct ukbd_translation *table, size_t tsize, 453 uint8_t keycode) 454 { 455 for (; tsize != 0; table++, tsize--) 456 if (table->original == keycode) 457 return table->translation; 458 return 0; 459 } 460 461 void 462 ukbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen) 463 { 464 struct ukbd_softc *sc = vsc; 465 struct hidkbd *kbd = &sc->sc_kbd; 466 uint8_t *pos, *spos, *epos, xlat; 467 468 static const struct ukbd_translation apple_fn_trans[] = { 469 { 40, 73 }, /* return -> insert */ 470 { 42, 76 }, /* backspace -> delete */ 471 #ifdef notyet 472 { 58, 0 }, /* F1 -> screen brightness down */ 473 { 59, 0 }, /* F2 -> screen brightness up */ 474 { 60, 0 }, /* F3 */ 475 { 61, 0 }, /* F4 */ 476 { 62, 0 }, /* F5 -> keyboard backlight down */ 477 { 63, 0 }, /* F6 -> keyboard backlight up */ 478 { 64, 0 }, /* F7 -> audio back */ 479 { 65, 0 }, /* F8 -> audio pause/play */ 480 { 66, 0 }, /* F9 -> audio next */ 481 #endif 482 #ifdef __macppc__ 483 { 60, 127 }, /* F3 -> audio mute */ 484 { 61, 129 }, /* F4 -> audio lower */ 485 { 62, 128 }, /* F5 -> audio raise */ 486 #else 487 { 67, 127 }, /* F10 -> audio mute */ 488 { 68, 129 }, /* F11 -> audio lower */ 489 { 69, 128 }, /* F12 -> audio raise */ 490 #endif 491 { 79, 77 }, /* right -> end */ 492 { 80, 74 }, /* left -> home */ 493 { 81, 78 }, /* down -> page down */ 494 { 82, 75 } /* up -> page up */ 495 }; 496 497 if (!hid_get_data(ibuf, &sc->sc_apple_fn)) 498 return; 499 500 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 501 epos = spos + kbd->sc_nkeycode; 502 503 for (pos = spos; pos != epos; pos++) { 504 xlat = ukbd_translate(apple_fn_trans, 505 nitems(apple_fn_trans), *pos); 506 if (xlat != 0) 507 *pos = xlat; 508 } 509 } 510 511 void 512 ukbd_apple_iso_munge(void *vsc, uint8_t *ibuf, u_int ilen) 513 { 514 struct ukbd_softc *sc = vsc; 515 struct hidkbd *kbd = &sc->sc_kbd; 516 uint8_t *pos, *spos, *epos, xlat; 517 518 static const struct ukbd_translation apple_iso_trans[] = { 519 { 53, 100 }, /* less -> grave */ 520 { 100, 53 }, 521 }; 522 523 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 524 epos = spos + kbd->sc_nkeycode; 525 526 for (pos = spos; pos != epos; pos++) { 527 xlat = ukbd_translate(apple_iso_trans, 528 nitems(apple_iso_trans), *pos); 529 if (xlat != 0) 530 *pos = xlat; 531 } 532 533 ukbd_apple_munge(vsc, ibuf, ilen); 534 } 535 536 #ifdef __loongson__ 537 /* 538 * Software Fn- translation for Gdium Liberty keyboard. 539 */ 540 #define GDIUM_FN_CODE 0x82 541 void 542 ukbd_gdium_munge(void *vsc, uint8_t *ibuf, u_int ilen) 543 { 544 struct ukbd_softc *sc = vsc; 545 struct hidkbd *kbd = &sc->sc_kbd; 546 uint8_t *pos, *spos, *epos, xlat; 547 int fn; 548 549 static const struct ukbd_translation gdium_fn_trans[] = { 550 #ifdef notyet 551 { 58, 0 }, /* F1 -> toggle camera */ 552 { 59, 0 }, /* F2 -> toggle wireless */ 553 #endif 554 { 60, 127 }, /* F3 -> audio mute */ 555 { 61, 128 }, /* F4 -> audio raise */ 556 { 62, 129 }, /* F5 -> audio lower */ 557 #ifdef notyet 558 { 63, 0 }, /* F6 -> toggle ext. video */ 559 { 64, 0 }, /* F7 -> toggle mouse */ 560 { 65, 0 }, /* F8 -> brightness up */ 561 { 66, 0 }, /* F9 -> brightness down */ 562 { 67, 0 }, /* F10 -> suspend */ 563 { 68, 0 }, /* F11 -> user1 */ 564 { 69, 0 }, /* F12 -> user2 */ 565 { 70, 0 }, /* print screen -> sysrq */ 566 #endif 567 { 76, 71 }, /* delete -> scroll lock */ 568 { 81, 78 }, /* down -> page down */ 569 { 82, 75 } /* up -> page up */ 570 }; 571 572 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 573 epos = spos + kbd->sc_nkeycode; 574 575 /* 576 * Check for Fn key being down and remove it from the report. 577 */ 578 579 fn = 0; 580 for (pos = spos; pos != epos; pos++) 581 if (*pos == GDIUM_FN_CODE) { 582 fn = 1; 583 *pos = 0; 584 break; 585 } 586 587 /* 588 * Rewrite keycodes on the fly to perform Fn-key translation. 589 * Keycodes without a translation are passed unaffected. 590 */ 591 592 if (fn != 0) 593 for (pos = spos; pos != epos; pos++) { 594 xlat = ukbd_translate(gdium_fn_trans, 595 nitems(gdium_fn_trans), *pos); 596 if (xlat != 0) 597 *pos = xlat; 598 } 599 600 } 601 #endif 602