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