1 /* $OpenBSD: ukbd.c,v 1.58 2012/07/13 12:33:08 shadchin 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 struct hidkbd sc_kbd; 130 131 int sc_spl; 132 133 u_char sc_dying; 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 usb_attach_arg *uaa = aux; 195 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 196 int size; 197 void *desc; 198 199 uhidev_get_report_desc(uha->parent, &desc, &size); 200 if (!hid_is_collection(desc, size, uha->reportid, 201 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 202 return (UMATCH_NONE); 203 204 return (UMATCH_IFACECLASS); 205 } 206 207 void 208 ukbd_attach(struct device *parent, struct device *self, void *aux) 209 { 210 struct ukbd_softc *sc = (struct ukbd_softc *)self; 211 struct hidkbd *kbd = &sc->sc_kbd; 212 struct usb_attach_arg *uaa = aux; 213 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 214 usb_hid_descriptor_t *hid; 215 u_int32_t qflags; 216 int dlen, repid; 217 void *desc; 218 kbd_t layout = (kbd_t)-1; 219 220 sc->sc_hdev.sc_intr = ukbd_intr; 221 sc->sc_hdev.sc_parent = uha->parent; 222 sc->sc_hdev.sc_report_id = uha->reportid; 223 224 uhidev_get_report_desc(uha->parent, &desc, &dlen); 225 repid = uha->reportid; 226 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 227 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 228 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 229 230 qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; 231 if (hidkbd_attach(self, kbd, 1, qflags, repid, desc, dlen) != 0) 232 return; 233 234 if (uha->uaa->vendor == USB_VENDOR_APPLE) { 235 int iso = 0; 236 237 if ((uha->uaa->product == USB_PRODUCT_APPLE_FOUNTAIN_ISO) || 238 (uha->uaa->product == USB_PRODUCT_APPLE_GEYSER_ISO)) 239 iso = 1; 240 241 if (hid_locate(desc, dlen, HID_USAGE2(HUP_APPLE, HUG_FN_KEY), 242 uha->reportid, hid_input, &sc->sc_apple_fn, &qflags)) { 243 if (qflags & HIO_VARIABLE) { 244 if (iso) 245 sc->sc_munge = ukbd_apple_iso_munge; 246 else 247 sc->sc_munge = ukbd_apple_munge; 248 } 249 } 250 } 251 252 if (uha->uaa->vendor == USB_VENDOR_TOPRE && 253 uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) { 254 /* ignore country code on purpose */ 255 } else { 256 hid = usbd_get_hid_descriptor(uha->uaa->iface); 257 258 if (hid->bCountryCode <= HCC_MAX) 259 layout = ukbd_countrylayout[hid->bCountryCode]; 260 #ifdef DIAGNOSTIC 261 if (hid->bCountryCode != 0) 262 printf(", country code %d", hid->bCountryCode); 263 #endif 264 } 265 if (layout == (kbd_t)-1) { 266 #ifdef UKBD_LAYOUT 267 layout = UKBD_LAYOUT; 268 #else 269 layout = KB_US; 270 #endif 271 } 272 273 printf("\n"); 274 275 #ifdef __loongson__ 276 if (uha->uaa->vendor == USB_VENDOR_CYPRESS && 277 uha->uaa->product == USB_PRODUCT_CYPRESS_LPRDK) 278 sc->sc_munge = ukbd_gdium_munge; 279 #endif 280 281 if (kbd->sc_console_keyboard) { 282 extern struct wskbd_mapdata ukbd_keymapdata; 283 284 DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc)); 285 ukbd_keymapdata.layout = layout; 286 wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); 287 ukbd_enable(sc, 1); 288 } 289 290 /* Flash the leds; no real purpose, just shows we're alive. */ 291 ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | 292 WSKBD_LED_CAPS | WSKBD_LED_COMPOSE); 293 usbd_delay_ms(uha->parent->sc_udev, 400); 294 ukbd_set_leds(sc, 0); 295 296 hidkbd_attach_wskbd(kbd, layout, &ukbd_accessops); 297 } 298 299 int 300 ukbd_activate(struct device *self, int act) 301 { 302 struct ukbd_softc *sc = (struct ukbd_softc *)self; 303 struct hidkbd *kbd = &sc->sc_kbd; 304 int rv = 0; 305 306 switch (act) { 307 case DVACT_DEACTIVATE: 308 if (kbd->sc_wskbddev != NULL) 309 rv = config_deactivate(kbd->sc_wskbddev); 310 sc->sc_dying = 1; 311 break; 312 } 313 return (rv); 314 } 315 316 int 317 ukbd_detach(struct device *self, int flags) 318 { 319 struct ukbd_softc *sc = (struct ukbd_softc *)self; 320 struct hidkbd *kbd = &sc->sc_kbd; 321 int rv; 322 323 rv = hidkbd_detach(kbd, flags); 324 325 /* The console keyboard does not get a disable call, so check pipe. */ 326 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 327 uhidev_close(&sc->sc_hdev); 328 329 return (rv); 330 } 331 332 void 333 ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) 334 { 335 struct ukbd_softc *sc = (struct ukbd_softc *)addr; 336 struct hidkbd *kbd = &sc->sc_kbd; 337 338 if (kbd->sc_enabled != 0) { 339 if (sc->sc_munge != NULL) 340 (*sc->sc_munge)(sc, (uint8_t *)ibuf, len); 341 hidkbd_input(kbd, (uint8_t *)ibuf, len); 342 } 343 } 344 345 int 346 ukbd_enable(void *v, int on) 347 { 348 struct ukbd_softc *sc = v; 349 struct hidkbd *kbd = &sc->sc_kbd; 350 int rv; 351 352 if (on && sc->sc_dying) 353 return EIO; 354 355 if ((rv = hidkbd_enable(kbd, on)) != 0) 356 return rv; 357 358 if (on) { 359 return uhidev_open(&sc->sc_hdev); 360 } else { 361 uhidev_close(&sc->sc_hdev); 362 return 0; 363 } 364 } 365 366 void 367 ukbd_set_leds(void *v, int leds) 368 { 369 struct ukbd_softc *sc = v; 370 struct hidkbd *kbd = &sc->sc_kbd; 371 u_int8_t res; 372 373 if (sc->sc_dying) 374 return; 375 376 if (hidkbd_set_leds(kbd, leds, &res) != 0) 377 uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, 378 &res, 1); 379 } 380 381 int 382 ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 383 { 384 struct ukbd_softc *sc = v; 385 struct hidkbd *kbd = &sc->sc_kbd; 386 int rc; 387 388 switch (cmd) { 389 case WSKBDIO_GTYPE: 390 *(int *)data = WSKBD_TYPE_USB; 391 return (0); 392 case WSKBDIO_SETLEDS: 393 ukbd_set_leds(v, *(int *)data); 394 return (0); 395 default: 396 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 397 if (rc != -1) 398 return rc; 399 else 400 return hidkbd_ioctl(kbd, cmd, data, flag, p); 401 } 402 } 403 404 /* Console interface. */ 405 void 406 ukbd_cngetc(void *v, u_int *type, int *data) 407 { 408 struct ukbd_softc *sc = v; 409 struct hidkbd *kbd = &sc->sc_kbd; 410 411 DPRINTFN(0,("ukbd_cngetc: enter\n")); 412 kbd->sc_polling = 1; 413 while (kbd->sc_npollchar <= 0) 414 usbd_dopoll(sc->sc_hdev.sc_parent->sc_iface); 415 kbd->sc_polling = 0; 416 hidkbd_cngetc(kbd, type, data); 417 DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", *data)); 418 } 419 420 void 421 ukbd_cnpollc(void *v, int on) 422 { 423 struct ukbd_softc *sc = v; 424 usbd_device_handle dev; 425 426 DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); 427 428 usbd_interface2device_handle(sc->sc_hdev.sc_parent->sc_iface, &dev); 429 if (on) 430 sc->sc_spl = splusb(); 431 else 432 splx(sc->sc_spl); 433 usbd_set_polling(dev, on); 434 } 435 436 void 437 ukbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 438 { 439 hidkbd_bell(pitch, period, volume, 1); 440 } 441 442 int 443 ukbd_cnattach(void) 444 { 445 446 /* 447 * XXX USB requires too many parts of the kernel to be running 448 * XXX in order to work, so we can't do much for the console 449 * XXX keyboard until autconfiguration has run its course. 450 */ 451 hidkbd_is_console = 1; 452 return (0); 453 } 454 455 uint8_t 456 ukbd_translate(const struct ukbd_translation *table, size_t tsize, 457 uint8_t keycode) 458 { 459 for (; tsize != 0; table++, tsize--) 460 if (table->original == keycode) 461 return table->translation; 462 return 0; 463 } 464 465 void 466 ukbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen) 467 { 468 struct ukbd_softc *sc = vsc; 469 struct hidkbd *kbd = &sc->sc_kbd; 470 uint8_t *pos, *spos, *epos, xlat; 471 472 static const struct ukbd_translation apple_fn_trans[] = { 473 { 40, 73 }, /* return -> insert */ 474 { 42, 76 }, /* backspace -> delete */ 475 #ifdef notyet 476 { 58, 0 }, /* F1 -> screen brightness down */ 477 { 59, 0 }, /* F2 -> screen brightness up */ 478 { 60, 0 }, /* F3 */ 479 { 61, 0 }, /* F4 */ 480 { 62, 0 }, /* F5 -> keyboard backlight down */ 481 { 63, 0 }, /* F6 -> keyboard backlight up */ 482 { 64, 0 }, /* F7 -> audio back */ 483 { 65, 0 }, /* F8 -> audio pause/play */ 484 { 66, 0 }, /* F9 -> audio next */ 485 #endif 486 #ifdef __macppc__ 487 { 60, 127 }, /* F3 -> audio mute */ 488 { 61, 129 }, /* F4 -> audio lower */ 489 { 62, 128 }, /* F5 -> audio raise */ 490 #else 491 { 67, 127 }, /* F10 -> audio mute */ 492 { 68, 129 }, /* F11 -> audio lower */ 493 { 69, 128 }, /* F12 -> audio raise */ 494 #endif 495 { 79, 77 }, /* right -> end */ 496 { 80, 74 }, /* left -> home */ 497 { 81, 78 }, /* down -> page down */ 498 { 82, 75 } /* up -> page up */ 499 }; 500 501 if (!hid_get_data(ibuf, &sc->sc_apple_fn)) 502 return; 503 504 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 505 epos = spos + kbd->sc_nkeycode; 506 507 for (pos = spos; pos != epos; pos++) { 508 xlat = ukbd_translate(apple_fn_trans, 509 nitems(apple_fn_trans), *pos); 510 if (xlat != 0) 511 *pos = xlat; 512 } 513 } 514 515 void 516 ukbd_apple_iso_munge(void *vsc, uint8_t *ibuf, u_int ilen) 517 { 518 struct ukbd_softc *sc = vsc; 519 struct hidkbd *kbd = &sc->sc_kbd; 520 uint8_t *pos, *spos, *epos, xlat; 521 522 static const struct ukbd_translation apple_iso_trans[] = { 523 { 53, 100 }, /* less -> grave */ 524 { 100, 53 }, 525 }; 526 527 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 528 epos = spos + kbd->sc_nkeycode; 529 530 for (pos = spos; pos != epos; pos++) { 531 xlat = ukbd_translate(apple_iso_trans, 532 nitems(apple_iso_trans), *pos); 533 if (xlat != 0) 534 *pos = xlat; 535 } 536 537 ukbd_apple_munge(vsc, ibuf, ilen); 538 } 539 540 #ifdef __loongson__ 541 /* 542 * Software Fn- translation for Gdium Liberty keyboard. 543 */ 544 #define GDIUM_FN_CODE 0x82 545 void 546 ukbd_gdium_munge(void *vsc, uint8_t *ibuf, u_int ilen) 547 { 548 struct ukbd_softc *sc = vsc; 549 struct hidkbd *kbd = &sc->sc_kbd; 550 uint8_t *pos, *spos, *epos, xlat; 551 int fn; 552 553 static const struct ukbd_translation gdium_fn_trans[] = { 554 #ifdef notyet 555 { 58, 0 }, /* F1 -> toggle camera */ 556 { 59, 0 }, /* F2 -> toggle wireless */ 557 #endif 558 { 60, 127 }, /* F3 -> audio mute */ 559 { 61, 128 }, /* F4 -> audio raise */ 560 { 62, 129 }, /* F5 -> audio lower */ 561 #ifdef notyet 562 { 63, 0 }, /* F6 -> toggle ext. video */ 563 { 64, 0 }, /* F7 -> toggle mouse */ 564 { 65, 0 }, /* F8 -> brightness up */ 565 { 66, 0 }, /* F9 -> brightness down */ 566 { 67, 0 }, /* F10 -> suspend */ 567 { 68, 0 }, /* F11 -> user1 */ 568 { 69, 0 }, /* F12 -> user2 */ 569 { 70, 0 }, /* print screen -> sysrq */ 570 #endif 571 { 76, 71 }, /* delete -> scroll lock */ 572 { 81, 78 }, /* down -> page down */ 573 { 82, 75 } /* up -> page up */ 574 }; 575 576 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 577 epos = spos + kbd->sc_nkeycode; 578 579 /* 580 * Check for Fn key being down and remove it from the report. 581 */ 582 583 fn = 0; 584 for (pos = spos; pos != epos; pos++) 585 if (*pos == GDIUM_FN_CODE) { 586 fn = 1; 587 *pos = 0; 588 break; 589 } 590 591 /* 592 * Rewrite keycodes on the fly to perform Fn-key translation. 593 * Keycodes without a translation are passed unaffected. 594 */ 595 596 if (fn != 0) 597 for (pos = spos; pos != epos; pos++) { 598 xlat = ukbd_translate(gdium_fn_trans, 599 nitems(gdium_fn_trans), *pos); 600 if (xlat != 0) 601 *pos = xlat; 602 } 603 604 } 605 #endif 606