1 /* $OpenBSD: ukbd.c,v 1.55 2011/07/03 15:47:17 matthew 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 void (*sc_munge)(void *, uint8_t *, u_int); 136 }; 137 138 void ukbd_cngetc(void *, u_int *, int *); 139 void ukbd_cnpollc(void *, int); 140 void ukbd_cnbell(void *, u_int, u_int, u_int); 141 142 const struct wskbd_consops ukbd_consops = { 143 ukbd_cngetc, 144 ukbd_cnpollc, 145 ukbd_cnbell, 146 }; 147 148 void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len); 149 150 int ukbd_enable(void *, int); 151 void ukbd_set_leds(void *, int); 152 int ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *); 153 154 const struct wskbd_accessops ukbd_accessops = { 155 ukbd_enable, 156 ukbd_set_leds, 157 ukbd_ioctl, 158 }; 159 160 int ukbd_match(struct device *, void *, void *); 161 void ukbd_attach(struct device *, struct device *, void *); 162 int ukbd_detach(struct device *, int); 163 int ukbd_activate(struct device *, int); 164 165 struct cfdriver ukbd_cd = { 166 NULL, "ukbd", DV_DULL 167 }; 168 169 const struct cfattach ukbd_ca = { 170 sizeof(struct ukbd_softc), 171 ukbd_match, 172 ukbd_attach, 173 ukbd_detach, 174 ukbd_activate, 175 }; 176 177 struct ukbd_translation { 178 uint8_t original; 179 uint8_t translation; 180 }; 181 182 #ifdef __loongson__ 183 void ukbd_gdium_munge(void *, uint8_t *, u_int); 184 #endif 185 uint8_t ukbd_translate(const struct ukbd_translation *, size_t, uint8_t); 186 187 int 188 ukbd_match(struct device *parent, void *match, void *aux) 189 { 190 struct usb_attach_arg *uaa = aux; 191 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 192 int size; 193 void *desc; 194 195 uhidev_get_report_desc(uha->parent, &desc, &size); 196 if (!hid_is_collection(desc, size, uha->reportid, 197 HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD))) 198 return (UMATCH_NONE); 199 200 return (UMATCH_IFACECLASS); 201 } 202 203 void 204 ukbd_attach(struct device *parent, struct device *self, void *aux) 205 { 206 struct ukbd_softc *sc = (struct ukbd_softc *)self; 207 struct hidkbd *kbd = &sc->sc_kbd; 208 struct usb_attach_arg *uaa = aux; 209 struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; 210 usb_hid_descriptor_t *hid; 211 u_int32_t qflags; 212 int dlen, repid; 213 void *desc; 214 kbd_t layout = (kbd_t)-1; 215 216 sc->sc_hdev.sc_intr = ukbd_intr; 217 sc->sc_hdev.sc_parent = uha->parent; 218 sc->sc_hdev.sc_report_id = uha->reportid; 219 220 uhidev_get_report_desc(uha->parent, &desc, &dlen); 221 repid = uha->reportid; 222 sc->sc_hdev.sc_isize = hid_report_size(desc, dlen, hid_input, repid); 223 sc->sc_hdev.sc_osize = hid_report_size(desc, dlen, hid_output, repid); 224 sc->sc_hdev.sc_fsize = hid_report_size(desc, dlen, hid_feature, repid); 225 226 qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; 227 if (hidkbd_attach(self, kbd, 1, qflags, repid, desc, dlen) != 0) 228 return; 229 230 if (uha->uaa->vendor == USB_VENDOR_TOPRE && 231 uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) { 232 /* ignore country code on purpose */ 233 } else { 234 hid = usbd_get_hid_descriptor(uha->uaa->iface); 235 236 if (hid->bCountryCode <= HCC_MAX) 237 layout = ukbd_countrylayout[hid->bCountryCode]; 238 #ifdef DIAGNOSTIC 239 if (hid->bCountryCode != 0) 240 printf(", country code %d", hid->bCountryCode); 241 #endif 242 } 243 if (layout == (kbd_t)-1) { 244 #ifdef UKBD_LAYOUT 245 layout = UKBD_LAYOUT; 246 #else 247 layout = KB_US; 248 #endif 249 } 250 251 printf("\n"); 252 253 #ifdef __loongson__ 254 if (uha->uaa->vendor == USB_VENDOR_CYPRESS && 255 uha->uaa->product == USB_PRODUCT_CYPRESS_LPRDK) 256 sc->sc_munge = ukbd_gdium_munge; 257 #endif 258 259 if (kbd->sc_console_keyboard) { 260 extern struct wskbd_mapdata ukbd_keymapdata; 261 262 DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc)); 263 ukbd_keymapdata.layout = layout; 264 wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata); 265 ukbd_enable(sc, 1); 266 } 267 268 /* Flash the leds; no real purpose, just shows we're alive. */ 269 ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | WSKBD_LED_CAPS); 270 usbd_delay_ms(uha->parent->sc_udev, 400); 271 ukbd_set_leds(sc, 0); 272 273 hidkbd_attach_wskbd(kbd, layout, &ukbd_accessops); 274 } 275 276 int 277 ukbd_activate(struct device *self, int act) 278 { 279 struct ukbd_softc *sc = (struct ukbd_softc *)self; 280 struct hidkbd *kbd = &sc->sc_kbd; 281 int rv = 0; 282 283 switch (act) { 284 case DVACT_DEACTIVATE: 285 if (kbd->sc_wskbddev != NULL) 286 rv = config_deactivate(kbd->sc_wskbddev); 287 sc->sc_dying = 1; 288 break; 289 } 290 return (rv); 291 } 292 293 int 294 ukbd_detach(struct device *self, int flags) 295 { 296 struct ukbd_softc *sc = (struct ukbd_softc *)self; 297 struct hidkbd *kbd = &sc->sc_kbd; 298 int rv; 299 300 rv = hidkbd_detach(kbd, flags); 301 302 /* The console keyboard does not get a disable call, so check pipe. */ 303 if (sc->sc_hdev.sc_state & UHIDEV_OPEN) 304 uhidev_close(&sc->sc_hdev); 305 306 return (rv); 307 } 308 309 void 310 ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) 311 { 312 struct ukbd_softc *sc = (struct ukbd_softc *)addr; 313 struct hidkbd *kbd = &sc->sc_kbd; 314 315 if (kbd->sc_enabled != 0) { 316 if (sc->sc_munge != NULL) 317 (*sc->sc_munge)(sc, (uint8_t *)ibuf, len); 318 hidkbd_input(kbd, (uint8_t *)ibuf, len); 319 } 320 } 321 322 int 323 ukbd_enable(void *v, int on) 324 { 325 struct ukbd_softc *sc = v; 326 struct hidkbd *kbd = &sc->sc_kbd; 327 int rv; 328 329 if (on && sc->sc_dying) 330 return EIO; 331 332 if ((rv = hidkbd_enable(kbd, on)) != 0) 333 return rv; 334 335 if (on) { 336 return uhidev_open(&sc->sc_hdev); 337 } else { 338 uhidev_close(&sc->sc_hdev); 339 return 0; 340 } 341 } 342 343 void 344 ukbd_set_leds(void *v, int leds) 345 { 346 struct ukbd_softc *sc = v; 347 struct hidkbd *kbd = &sc->sc_kbd; 348 u_int8_t res; 349 350 if (sc->sc_dying) 351 return; 352 353 if (hidkbd_set_leds(kbd, leds, &res) != 0) 354 uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, 355 &res, 1); 356 } 357 358 int 359 ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p) 360 { 361 struct ukbd_softc *sc = v; 362 struct hidkbd *kbd = &sc->sc_kbd; 363 int rc; 364 365 switch (cmd) { 366 case WSKBDIO_GTYPE: 367 *(int *)data = WSKBD_TYPE_USB; 368 return (0); 369 case WSKBDIO_SETLEDS: 370 ukbd_set_leds(v, *(int *)data); 371 return (0); 372 default: 373 rc = uhidev_ioctl(&sc->sc_hdev, cmd, data, flag, p); 374 if (rc != -1) 375 return rc; 376 else 377 return hidkbd_ioctl(kbd, cmd, data, flag, p); 378 } 379 } 380 381 /* Console interface. */ 382 void 383 ukbd_cngetc(void *v, u_int *type, int *data) 384 { 385 struct ukbd_softc *sc = v; 386 struct hidkbd *kbd = &sc->sc_kbd; 387 388 DPRINTFN(0,("ukbd_cngetc: enter\n")); 389 kbd->sc_polling = 1; 390 while (kbd->sc_npollchar <= 0) 391 usbd_dopoll(sc->sc_hdev.sc_parent->sc_iface); 392 kbd->sc_polling = 0; 393 hidkbd_cngetc(kbd, type, data); 394 DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", *data)); 395 } 396 397 void 398 ukbd_cnpollc(void *v, int on) 399 { 400 struct ukbd_softc *sc = v; 401 usbd_device_handle dev; 402 403 DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on)); 404 405 usbd_interface2device_handle(sc->sc_hdev.sc_parent->sc_iface, &dev); 406 if (on) 407 sc->sc_spl = splusb(); 408 else 409 splx(sc->sc_spl); 410 usbd_set_polling(dev, on); 411 } 412 413 void 414 ukbd_cnbell(void *v, u_int pitch, u_int period, u_int volume) 415 { 416 hidkbd_bell(pitch, period, volume, 1); 417 } 418 419 int 420 ukbd_cnattach(void) 421 { 422 423 /* 424 * XXX USB requires too many parts of the kernel to be running 425 * XXX in order to work, so we can't do much for the console 426 * XXX keyboard until autconfiguration has run its course. 427 */ 428 hidkbd_is_console = 1; 429 return (0); 430 } 431 432 uint8_t 433 ukbd_translate(const struct ukbd_translation *table, size_t tsize, 434 uint8_t keycode) 435 { 436 for (; tsize != 0; table++, tsize--) 437 if (table->original == keycode) 438 return table->translation; 439 return 0; 440 } 441 442 #ifdef __loongson__ 443 /* 444 * Software Fn- translation for Gdium Liberty keyboard. 445 */ 446 #define GDIUM_FN_CODE 0x82 447 void 448 ukbd_gdium_munge(void *vsc, uint8_t *ibuf, u_int ilen) 449 { 450 struct ukbd_softc *sc = vsc; 451 struct hidkbd *kbd = &sc->sc_kbd; 452 uint8_t *pos, *spos, *epos, xlat; 453 int fn; 454 455 static const struct ukbd_translation gdium_fn_trans[] = { 456 #ifdef notyet 457 { 58, 0 }, /* F1 -> toggle camera */ 458 { 59, 0 }, /* F2 -> toggle wireless */ 459 #endif 460 { 60, 127 }, /* F3 -> audio mute */ 461 { 61, 128 }, /* F4 -> audio raise */ 462 { 62, 129 }, /* F5 -> audio lower */ 463 #ifdef notyet 464 { 63, 0 }, /* F6 -> toggle ext. video */ 465 { 64, 0 }, /* F7 -> toggle mouse */ 466 { 65, 0 }, /* F8 -> brightness up */ 467 { 66, 0 }, /* F9 -> brightness down */ 468 { 67, 0 }, /* F10 -> suspend */ 469 { 68, 0 }, /* F11 -> user1 */ 470 { 69, 0 }, /* F12 -> user2 */ 471 { 70, 0 }, /* print screen -> sysrq */ 472 #endif 473 { 76, 71 }, /* delete -> scroll lock */ 474 { 81, 78 }, /* down -> page down */ 475 { 82, 75 } /* up -> page up */ 476 }; 477 478 spos = ibuf + kbd->sc_keycodeloc.pos / 8; 479 epos = spos + kbd->sc_nkeycode; 480 481 /* 482 * Check for Fn key being down and remove it from the report. 483 */ 484 485 fn = 0; 486 for (pos = spos; pos != epos; pos++) 487 if (*pos == GDIUM_FN_CODE) { 488 fn = 1; 489 *pos = 0; 490 break; 491 } 492 493 /* 494 * Rewrite keycodes on the fly to perform Fn-key translation. 495 * Keycodes without a translation are passed unaffected. 496 */ 497 498 if (fn != 0) 499 for (pos = spos; pos != epos; pos++) { 500 xlat = ukbd_translate(gdium_fn_trans, 501 nitems(gdium_fn_trans), *pos); 502 if (xlat != 0) 503 *pos = xlat; 504 } 505 506 } 507 #endif 508