Annotation of sys/dev/usb/ukbd.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ukbd.c,v 1.36 2007/06/14 10:11:16 mbalmer Exp $ */
! 2: /* $NetBSD: ukbd.c,v 1.85 2003/03/11 16:44:00 augustss Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1998 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by Lennart Augustsson (lennart@augustsson.net) at
! 10: * Carlstedt Research & Technology.
! 11: *
! 12: * Redistribution and use in source and binary forms, with or without
! 13: * modification, are permitted provided that the following conditions
! 14: * are met:
! 15: * 1. Redistributions of source code must retain the above copyright
! 16: * notice, this list of conditions and the following disclaimer.
! 17: * 2. Redistributions in binary form must reproduce the above copyright
! 18: * notice, this list of conditions and the following disclaimer in the
! 19: * documentation and/or other materials provided with the distribution.
! 20: * 3. All advertising materials mentioning features or use of this software
! 21: * must display the following acknowledgement:
! 22: * This product includes software developed by the NetBSD
! 23: * Foundation, Inc. and its contributors.
! 24: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 25: * contributors may be used to endorse or promote products derived
! 26: * from this software without specific prior written permission.
! 27: *
! 28: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 29: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 30: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 31: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 32: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 33: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 34: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 35: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 36: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 37: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 38: * POSSIBILITY OF SUCH DAMAGE.
! 39: */
! 40:
! 41: /*
! 42: * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
! 43: */
! 44:
! 45: #include <sys/param.h>
! 46: #include <sys/systm.h>
! 47: #include <sys/timeout.h>
! 48: #include <sys/kernel.h>
! 49: #include <sys/device.h>
! 50: #include <sys/ioctl.h>
! 51: #include <sys/tty.h>
! 52: #include <sys/file.h>
! 53: #include <sys/selinfo.h>
! 54: #include <sys/proc.h>
! 55: #include <sys/vnode.h>
! 56: #include <sys/poll.h>
! 57:
! 58: #include <dev/usb/usb.h>
! 59: #include <dev/usb/usbhid.h>
! 60:
! 61: #include <dev/usb/usbdi.h>
! 62: #include <dev/usb/usbdi_util.h>
! 63: #include <dev/usb/usbdevs.h>
! 64: #include <dev/usb/usb_quirks.h>
! 65: #include <dev/usb/uhidev.h>
! 66: #include <dev/usb/hid.h>
! 67: #include <dev/usb/ukbdvar.h>
! 68:
! 69: #include <dev/wscons/wsconsio.h>
! 70: #include <dev/wscons/wskbdvar.h>
! 71: #include <dev/wscons/wsksymdef.h>
! 72: #include <dev/wscons/wsksymvar.h>
! 73:
! 74: #ifdef UKBD_DEBUG
! 75: #define DPRINTF(x) do { if (ukbddebug) printf x; } while (0)
! 76: #define DPRINTFN(n,x) do { if (ukbddebug>(n)) printf x; } while (0)
! 77: int ukbddebug = 0;
! 78: #else
! 79: #define DPRINTF(x)
! 80: #define DPRINTFN(n,x)
! 81: #endif
! 82:
! 83: #define MAXKEYCODE 6
! 84: #define MAXMOD 8 /* max 32 */
! 85:
! 86: struct ukbd_data {
! 87: u_int32_t modifiers;
! 88: u_int8_t keycode[MAXKEYCODE];
! 89: };
! 90:
! 91: #define PRESS 0x000
! 92: #define RELEASE 0x100
! 93: #define CODEMASK 0x0ff
! 94:
! 95: #if defined(WSDISPLAY_COMPAT_RAWKBD)
! 96: #define NN 0 /* no translation */
! 97: /*
! 98: * Translate USB keycodes to US keyboard XT scancodes.
! 99: * Scancodes >= 0x80 represent EXTENDED keycodes.
! 100: *
! 101: * See http://www.microsoft.com/whdc/device/input/Scancode.mspx
! 102: */
! 103: const u_int8_t ukbd_trtab[256] = {
! 104: NN, NN, NN, NN, 0x1e, 0x30, 0x2e, 0x20, /* 00 - 07 */
! 105: 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, /* 08 - 0f */
! 106: 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, /* 10 - 17 */
! 107: 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, /* 18 - 1f */
! 108: 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, /* 20 - 27 */
! 109: 0x1c, 0x01, 0x0e, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, /* 28 - 2f */
! 110: 0x1b, 0x2b, 0x2b, 0x27, 0x28, 0x29, 0x33, 0x34, /* 30 - 37 */
! 111: 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, /* 38 - 3f */
! 112: 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xaa, 0x46, /* 40 - 47 */
! 113: 0x7f, 0xd2, 0xc7, 0xc9, 0xd3, 0xcf, 0xd1, 0xcd, /* 48 - 4f */
! 114: 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, /* 50 - 57 */
! 115: 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, /* 58 - 5f */
! 116: 0x48, 0x49, 0x52, 0x53, 0x56, 0xdd, NN, 0x59, /* 60 - 67 */
! 117: 0x5d, 0x5e, 0x5f, NN, NN, NN, NN, NN, /* 68 - 6f */
! 118: NN, NN, NN, NN, 0x97, NN, 0x93, 0x95, /* 70 - 77 */
! 119: 0x91, 0x92, 0x94, 0x9a, 0x96, 0x98, 0x99, NN, /* 78 - 7f */
! 120: NN, NN, NN, NN, NN, 0x7e, NN, 0x73, /* 80 - 87 */
! 121: 0x70, 0x7d, 0x79, 0x7b, 0x5c, NN, NN, NN, /* 88 - 8f */
! 122: NN, NN, 0x78, 0x77, 0x76, NN, NN, NN, /* 90 - 97 */
! 123: NN, NN, NN, NN, NN, NN, NN, NN, /* 98 - 9f */
! 124: NN, NN, NN, NN, NN, NN, NN, NN, /* a0 - a7 */
! 125: NN, NN, NN, NN, NN, NN, NN, NN, /* a8 - af */
! 126: NN, NN, NN, NN, NN, NN, NN, NN, /* b0 - b7 */
! 127: NN, NN, NN, NN, NN, NN, NN, NN, /* b8 - bf */
! 128: NN, NN, NN, NN, NN, NN, NN, NN, /* c0 - c7 */
! 129: NN, NN, NN, NN, NN, NN, NN, NN, /* c8 - cf */
! 130: NN, NN, NN, NN, NN, NN, NN, NN, /* d0 - d7 */
! 131: NN, NN, NN, NN, NN, NN, NN, NN, /* d8 - df */
! 132: 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, /* e0 - e7 */
! 133: NN, NN, NN, NN, NN, NN, NN, NN, /* e8 - ef */
! 134: NN, NN, NN, NN, NN, NN, NN, NN, /* f0 - f7 */
! 135: NN, NN, NN, NN, NN, NN, NN, NN, /* f8 - ff */
! 136: };
! 137: #endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */
! 138:
! 139: const kbd_t ukbd_countrylayout[HCC_MAX] = {
! 140: (kbd_t)-1,
! 141: (kbd_t)-1, /* arabic */
! 142: KB_BE, /* belgian */
! 143: (kbd_t)-1, /* canadian bilingual */
! 144: KB_CF, /* canadian french */
! 145: (kbd_t)-1, /* czech */
! 146: KB_DK, /* danish */
! 147: (kbd_t)-1, /* finnish */
! 148: KB_FR, /* french */
! 149: KB_DE, /* german */
! 150: (kbd_t)-1, /* greek */
! 151: (kbd_t)-1, /* hebrew */
! 152: KB_HU, /* hungary */
! 153: (kbd_t)-1, /* international (iso) */
! 154: KB_IT, /* italian */
! 155: KB_JP, /* japanese (katakana) */
! 156: (kbd_t)-1, /* korean */
! 157: KB_LA, /* latin american */
! 158: (kbd_t)-1, /* netherlands/dutch */
! 159: KB_NO, /* norwegian */
! 160: (kbd_t)-1, /* persian (farsi) */
! 161: KB_PL, /* polish */
! 162: KB_PT, /* portuguese */
! 163: KB_RU, /* russian */
! 164: (kbd_t)-1, /* slovakia */
! 165: KB_ES, /* spanish */
! 166: KB_SF, /* swiss french */
! 167: KB_SG, /* swiss german */
! 168: (kbd_t)-1, /* switzerland */
! 169: (kbd_t)-1, /* taiwan */
! 170: KB_TR, /* turkish Q */
! 171: KB_UK, /* uk */
! 172: KB_US, /* us */
! 173: (kbd_t)-1, /* yugoslavia */
! 174: (kbd_t)-1 /* turkish F */
! 175: };
! 176:
! 177: #define SUN_HCC_MIN 0x21
! 178: #define SUN_HCC_MAX 0x3f
! 179: const kbd_t ukbd_sunlayout[1 + SUN_HCC_MAX - SUN_HCC_MIN] = {
! 180: KB_US, /* 021 USA */
! 181: KB_US, /* 022 UNIX */
! 182: KB_FR, /* 023 France */
! 183: KB_DK, /* 024 Denmark */
! 184: KB_DE, /* 025 Germany */
! 185: KB_IT, /* 026 Italy */
! 186: KB_NL, /* 027 The Netherlands */
! 187: KB_NO, /* 028 Norway */
! 188: KB_PT, /* 029 Portugal */
! 189: KB_ES, /* 02a Spain */
! 190: KB_SV, /* 02b Sweden */
! 191: KB_SF, /* 02c Switzerland/French */
! 192: KB_SG, /* 02d Switzerland/German */
! 193: KB_UK, /* 02e Great Britain */
! 194: -1, /* 02f Korea */
! 195: -1, /* 030 Taiwan */
! 196: KB_JP, /* 031 Japan */
! 197: -1, /* 032 Canada/French */
! 198: -1, /* 033 Hungary */
! 199: -1, /* 034 Poland */
! 200: -1, /* 035 Czech */
! 201: -1, /* 036 Russia */
! 202: -1, /* 037 Latvia */
! 203: -1, /* 038 Turkey-Q5 */
! 204: -1, /* 039 Greece */
! 205: -1, /* 03a Arabic */
! 206: -1, /* 03b Lithuania */
! 207: -1, /* 03c Belgium */
! 208: -1, /* 03d unaffected */
! 209: -1, /* 03e Turkey-F5 */
! 210: -1, /* 03f Canada/French */
! 211: };
! 212:
! 213: #define KEY_ERROR 0x01
! 214:
! 215: #define MAXKEYS (MAXMOD+2*MAXKEYCODE)
! 216:
! 217: struct ukbd_softc {
! 218: struct uhidev sc_hdev;
! 219:
! 220: struct ukbd_data sc_ndata;
! 221: struct ukbd_data sc_odata;
! 222: struct hid_location sc_modloc[MAXMOD];
! 223: u_int sc_nmod;
! 224: struct {
! 225: u_int32_t mask;
! 226: u_int8_t key;
! 227: } sc_mods[MAXMOD];
! 228:
! 229: struct hid_location sc_keycodeloc;
! 230: u_int sc_nkeycode;
! 231:
! 232: char sc_enabled;
! 233:
! 234: int sc_console_keyboard; /* we are the console keyboard */
! 235:
! 236: char sc_debounce; /* for quirk handling */
! 237: struct timeout sc_delay; /* for quirk handling */
! 238: struct ukbd_data sc_data; /* for quirk handling */
! 239:
! 240: struct hid_location sc_numloc;
! 241: struct hid_location sc_capsloc;
! 242: struct hid_location sc_scroloc;
! 243: int sc_leds;
! 244:
! 245: struct timeout sc_rawrepeat_ch;
! 246:
! 247: struct device *sc_wskbddev;
! 248: #if defined(WSDISPLAY_COMPAT_RAWKBD)
! 249: #define REP_DELAY1 400
! 250: #define REP_DELAYN 100
! 251: int sc_rawkbd;
! 252: int sc_nrep;
! 253: char sc_rep[MAXKEYS];
! 254: #endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */
! 255:
! 256: int sc_spl;
! 257: int sc_polling;
! 258: int sc_npollchar;
! 259: u_int16_t sc_pollchars[MAXKEYS];
! 260:
! 261: u_char sc_dying;
! 262: };
! 263:
! 264: #ifdef UKBD_DEBUG
! 265: #define UKBDTRACESIZE 64
! 266: struct ukbdtraceinfo {
! 267: int unit;
! 268: struct timeval tv;
! 269: struct ukbd_data ud;
! 270: };
! 271: struct ukbdtraceinfo ukbdtracedata[UKBDTRACESIZE];
! 272: int ukbdtraceindex = 0;
! 273: int ukbdtrace = 0;
! 274: void ukbdtracedump(void);
! 275: void
! 276: ukbdtracedump(void)
! 277: {
! 278: int i;
! 279: for (i = 0; i < UKBDTRACESIZE; i++) {
! 280: struct ukbdtraceinfo *p =
! 281: &ukbdtracedata[(i+ukbdtraceindex)%UKBDTRACESIZE];
! 282: printf("%lu.%06lu: mod=0x%02x key0=0x%02x key1=0x%02x "
! 283: "key2=0x%02x key3=0x%02x\n",
! 284: p->tv.tv_sec, p->tv.tv_usec,
! 285: p->ud.modifiers, p->ud.keycode[0], p->ud.keycode[1],
! 286: p->ud.keycode[2], p->ud.keycode[3]);
! 287: }
! 288: }
! 289: #endif
! 290:
! 291: #define UKBDUNIT(dev) (minor(dev))
! 292: #define UKBD_CHUNK 128 /* chunk size for read */
! 293: #define UKBD_BSIZE 1020 /* buffer size */
! 294:
! 295: int ukbd_is_console;
! 296:
! 297: void ukbd_cngetc(void *, u_int *, int *);
! 298: void ukbd_cnpollc(void *, int);
! 299:
! 300: const struct wskbd_consops ukbd_consops = {
! 301: ukbd_cngetc,
! 302: ukbd_cnpollc,
! 303: };
! 304:
! 305: const char *ukbd_parse_desc(struct ukbd_softc *sc);
! 306:
! 307: void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len);
! 308: void ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud);
! 309: void ukbd_delayed_decode(void *addr);
! 310:
! 311: int ukbd_enable(void *, int);
! 312: void ukbd_set_leds(void *, int);
! 313:
! 314: int ukbd_ioctl(void *, u_long, caddr_t, int, struct proc *);
! 315: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 316: void ukbd_rawrepeat(void *v);
! 317: #endif
! 318:
! 319: const struct wskbd_accessops ukbd_accessops = {
! 320: ukbd_enable,
! 321: ukbd_set_leds,
! 322: ukbd_ioctl,
! 323: };
! 324:
! 325: extern const struct wscons_keydesc ukbd_keydesctab[];
! 326:
! 327: struct wskbd_mapdata ukbd_keymapdata = {
! 328: ukbd_keydesctab
! 329: };
! 330:
! 331: int ukbd_match(struct device *, void *, void *);
! 332: void ukbd_attach(struct device *, struct device *, void *);
! 333: int ukbd_detach(struct device *, int);
! 334: int ukbd_activate(struct device *, enum devact);
! 335:
! 336: struct cfdriver ukbd_cd = {
! 337: NULL, "ukbd", DV_DULL
! 338: };
! 339:
! 340: const struct cfattach ukbd_ca = {
! 341: sizeof(struct ukbd_softc),
! 342: ukbd_match,
! 343: ukbd_attach,
! 344: ukbd_detach,
! 345: ukbd_activate,
! 346: };
! 347:
! 348: int
! 349: ukbd_match(struct device *parent, void *match, void *aux)
! 350: {
! 351: struct usb_attach_arg *uaa = aux;
! 352: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
! 353: int size;
! 354: void *desc;
! 355:
! 356: uhidev_get_report_desc(uha->parent, &desc, &size);
! 357: if (!hid_is_collection(desc, size, uha->reportid,
! 358: HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD)))
! 359: return (UMATCH_NONE);
! 360:
! 361: return (UMATCH_IFACECLASS);
! 362: }
! 363:
! 364: void
! 365: ukbd_attach(struct device *parent, struct device *self, void *aux)
! 366: {
! 367: struct ukbd_softc *sc = (struct ukbd_softc *)self;
! 368: struct usb_attach_arg *uaa = aux;
! 369: struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa;
! 370: usb_hid_descriptor_t *hid;
! 371: u_int32_t qflags;
! 372: const char *parseerr;
! 373: kbd_t layout = (kbd_t)-1;
! 374: struct wskbddev_attach_args a;
! 375:
! 376: sc->sc_hdev.sc_intr = ukbd_intr;
! 377: sc->sc_hdev.sc_parent = uha->parent;
! 378: sc->sc_hdev.sc_report_id = uha->reportid;
! 379:
! 380: parseerr = ukbd_parse_desc(sc);
! 381: if (parseerr != NULL) {
! 382: printf("\n%s: attach failed, %s\n",
! 383: sc->sc_hdev.sc_dev.dv_xname, parseerr);
! 384: return;
! 385: }
! 386:
! 387: hid = usbd_get_hid_descriptor(uha->uaa->iface);
! 388:
! 389: #ifdef DIAGNOSTIC
! 390: printf(": %d modifier keys, %d key codes",
! 391: sc->sc_nmod, sc->sc_nkeycode);
! 392: #endif
! 393:
! 394: qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags;
! 395: sc->sc_debounce = (qflags & UQ_SPUR_BUT_UP) != 0;
! 396:
! 397: /*
! 398: * Remember if we're the console keyboard.
! 399: *
! 400: * XXX This always picks the first keyboard on the
! 401: * first USB bus, but what else can we really do?
! 402: */
! 403: if ((sc->sc_console_keyboard = ukbd_is_console) != 0) {
! 404: /* Don't let any other keyboard have it. */
! 405: ukbd_is_console = 0;
! 406: }
! 407:
! 408: if (uha->uaa->vendor == USB_VENDOR_SUN &&
! 409: (uha->uaa->product == USB_PRODUCT_SUN_KEYBOARD6 ||
! 410: uha->uaa->product == USB_PRODUCT_SUN_KEYBOARD7)) {
! 411: /* Sun keyboard use Sun-style layout codes */
! 412: if (hid->bCountryCode >= SUN_HCC_MIN &&
! 413: hid->bCountryCode <= SUN_HCC_MAX)
! 414: layout = ukbd_sunlayout[hid->bCountryCode - SUN_HCC_MIN];
! 415: #ifdef DIAGNOSTIC
! 416: if (hid->bCountryCode != 0)
! 417: printf(", layout %d", hid->bCountryCode);
! 418: #endif
! 419: } else {
! 420: if (uha->uaa->vendor == USB_VENDOR_TOPRE &&
! 421: uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) {
! 422: /* ignore country code on purpose */
! 423: } else {
! 424: if (hid->bCountryCode <= HCC_MAX)
! 425: layout = ukbd_countrylayout[hid->bCountryCode];
! 426: #ifdef DIAGNOSTIC
! 427: if (hid->bCountryCode != 0)
! 428: printf(", country code %d", hid->bCountryCode);
! 429: #endif
! 430: }
! 431: }
! 432: if (layout == (kbd_t)-1) {
! 433: #ifdef UKBD_LAYOUT
! 434: layout = UKBD_LAYOUT;
! 435: #else
! 436: layout = KB_US;
! 437: #endif
! 438: }
! 439: ukbd_keymapdata.layout = layout;
! 440:
! 441: printf("\n");
! 442:
! 443: if (sc->sc_console_keyboard) {
! 444: DPRINTF(("ukbd_attach: console keyboard sc=%p\n", sc));
! 445: wskbd_cnattach(&ukbd_consops, sc, &ukbd_keymapdata);
! 446: ukbd_enable(sc, 1);
! 447: }
! 448:
! 449: a.console = sc->sc_console_keyboard;
! 450:
! 451: a.keymap = &ukbd_keymapdata;
! 452:
! 453: a.accessops = &ukbd_accessops;
! 454: a.accesscookie = sc;
! 455:
! 456: timeout_set(&sc->sc_rawrepeat_ch, NULL, NULL);
! 457: timeout_set(&sc->sc_delay, NULL, NULL);
! 458:
! 459: /* Flash the leds; no real purpose, just shows we're alive. */
! 460: ukbd_set_leds(sc, WSKBD_LED_SCROLL | WSKBD_LED_NUM | WSKBD_LED_CAPS);
! 461: usbd_delay_ms(uha->parent->sc_udev, 400);
! 462: ukbd_set_leds(sc, 0);
! 463:
! 464: sc->sc_wskbddev = config_found(self, &a, wskbddevprint);
! 465: }
! 466:
! 467: int
! 468: ukbd_enable(void *v, int on)
! 469: {
! 470: struct ukbd_softc *sc = v;
! 471:
! 472: if (on && sc->sc_dying)
! 473: return (EIO);
! 474:
! 475: /* Should only be called to change state */
! 476: if (sc->sc_enabled == on) {
! 477: DPRINTF(("ukbd_enable: %s: bad call on=%d\n",
! 478: sc->sc_hdev.sc_dev.dv_xname, on));
! 479: return (EBUSY);
! 480: }
! 481:
! 482: DPRINTF(("ukbd_enable: sc=%p on=%d\n", sc, on));
! 483: sc->sc_enabled = on;
! 484: if (on) {
! 485: return (uhidev_open(&sc->sc_hdev));
! 486: } else {
! 487: uhidev_close(&sc->sc_hdev);
! 488: return (0);
! 489: }
! 490: }
! 491:
! 492: int
! 493: ukbd_activate(struct device *self, enum devact act)
! 494: {
! 495: struct ukbd_softc *sc = (struct ukbd_softc *)self;
! 496: int rv = 0;
! 497:
! 498: switch (act) {
! 499: case DVACT_ACTIVATE:
! 500: break;
! 501:
! 502: case DVACT_DEACTIVATE:
! 503: if (sc->sc_wskbddev != NULL)
! 504: rv = config_deactivate(sc->sc_wskbddev);
! 505: sc->sc_dying = 1;
! 506: break;
! 507: }
! 508: return (rv);
! 509: }
! 510:
! 511: int
! 512: ukbd_detach(struct device *self, int flags)
! 513: {
! 514: struct ukbd_softc *sc = (struct ukbd_softc *)self;
! 515: int rv = 0;
! 516:
! 517: DPRINTF(("ukbd_detach: sc=%p flags=%d\n", sc, flags));
! 518:
! 519: if (sc->sc_console_keyboard) {
! 520: #if 0
! 521: /*
! 522: * XXX Should probably disconnect our consops,
! 523: * XXX and either notify some other keyboard that
! 524: * XXX it can now be the console, or if there aren't
! 525: * XXX any more USB keyboards, set ukbd_is_console
! 526: * XXX back to 1 so that the next USB keyboard attached
! 527: * XXX to the system will get it.
! 528: */
! 529: panic("ukbd_detach: console keyboard");
! 530: #else
! 531: /*
! 532: * Disconnect our consops and set ukbd_is_console
! 533: * back to 1 so that the next USB keyboard attached
! 534: * to the system will get it.
! 535: * XXX Should notify some other keyboard that it can be
! 536: * XXX console, if there are any other keyboards.
! 537: */
! 538: printf("%s: was console keyboard\n",
! 539: sc->sc_hdev.sc_dev.dv_xname);
! 540: wskbd_cndetach();
! 541: ukbd_is_console = 1;
! 542: #endif
! 543: }
! 544: /* No need to do reference counting of ukbd, wskbd has all the goo. */
! 545: if (sc->sc_wskbddev != NULL)
! 546: rv = config_detach(sc->sc_wskbddev, flags);
! 547:
! 548: /* The console keyboard does not get a disable call, so check pipe. */
! 549: if (sc->sc_hdev.sc_state & UHIDEV_OPEN)
! 550: uhidev_close(&sc->sc_hdev);
! 551:
! 552: return (rv);
! 553: }
! 554:
! 555: void
! 556: ukbd_intr(struct uhidev *addr, void *ibuf, u_int len)
! 557: {
! 558: struct ukbd_softc *sc = (struct ukbd_softc *)addr;
! 559: struct ukbd_data *ud = &sc->sc_ndata;
! 560: int i;
! 561:
! 562: #ifdef UKBD_DEBUG
! 563: if (ukbddebug > 5) {
! 564: printf("ukbd_intr: data");
! 565: for (i = 0; i < len; i++)
! 566: printf(" 0x%02x", ((u_char *)ibuf)[i]);
! 567: printf("\n");
! 568: }
! 569: #endif
! 570:
! 571: ud->modifiers = 0;
! 572: for (i = 0; i < sc->sc_nmod; i++)
! 573: if (hid_get_data(ibuf, &sc->sc_modloc[i]))
! 574: ud->modifiers |= sc->sc_mods[i].mask;
! 575: memcpy(ud->keycode, (char *)ibuf + sc->sc_keycodeloc.pos / 8,
! 576: sc->sc_nkeycode);
! 577:
! 578: if (sc->sc_debounce && !sc->sc_polling) {
! 579: /*
! 580: * Some keyboards have a peculiar quirk. They sometimes
! 581: * generate a key up followed by a key down for the same
! 582: * key after about 10 ms.
! 583: * We avoid this bug by holding off decoding for 20 ms.
! 584: */
! 585: sc->sc_data = *ud;
! 586: timeout_del(&sc->sc_delay);
! 587: timeout_set(&sc->sc_delay, ukbd_delayed_decode, sc);
! 588: timeout_add(&sc->sc_delay, hz / 50);
! 589: #ifdef DDB
! 590: } else if (sc->sc_console_keyboard && !sc->sc_polling) {
! 591: /*
! 592: * For the console keyboard we can't deliver CTL-ALT-ESC
! 593: * from the interrupt routine. Doing so would start
! 594: * polling from inside the interrupt routine and that
! 595: * loses bigtime.
! 596: */
! 597: sc->sc_data = *ud;
! 598: timeout_del(&sc->sc_delay);
! 599: timeout_set(&sc->sc_delay, ukbd_delayed_decode, sc);
! 600: timeout_add(&sc->sc_delay, 1);
! 601: #endif
! 602: } else {
! 603: ukbd_decode(sc, ud);
! 604: }
! 605: }
! 606:
! 607: void
! 608: ukbd_delayed_decode(void *addr)
! 609: {
! 610: struct ukbd_softc *sc = addr;
! 611:
! 612: ukbd_decode(sc, &sc->sc_data);
! 613: }
! 614:
! 615: void
! 616: ukbd_decode(struct ukbd_softc *sc, struct ukbd_data *ud)
! 617: {
! 618: int mod, omod;
! 619: u_int16_t ibuf[MAXKEYS]; /* chars events */
! 620: int s;
! 621: int nkeys, i, j;
! 622: int key;
! 623: #define ADDKEY(c) ibuf[nkeys++] = (c)
! 624:
! 625: #ifdef UKBD_DEBUG
! 626: /*
! 627: * Keep a trace of the last events. Using printf changes the
! 628: * timing, so this can be useful sometimes.
! 629: */
! 630: if (ukbdtrace) {
! 631: struct ukbdtraceinfo *p = &ukbdtracedata[ukbdtraceindex];
! 632: p->unit = sc->sc_hdev.sc_dev.dv_unit;
! 633: microtime(&p->tv);
! 634: p->ud = *ud;
! 635: if (++ukbdtraceindex >= UKBDTRACESIZE)
! 636: ukbdtraceindex = 0;
! 637: }
! 638: if (ukbddebug > 5) {
! 639: struct timeval tv;
! 640: microtime(&tv);
! 641: DPRINTF((" at %lu.%06lu mod=0x%02x key0=0x%02x key1=0x%02x "
! 642: "key2=0x%02x key3=0x%02x\n",
! 643: tv.tv_sec, tv.tv_usec,
! 644: ud->modifiers, ud->keycode[0], ud->keycode[1],
! 645: ud->keycode[2], ud->keycode[3]));
! 646: }
! 647: #endif
! 648:
! 649: if (ud->keycode[0] == KEY_ERROR) {
! 650: DPRINTF(("ukbd_intr: KEY_ERROR\n"));
! 651: return; /* ignore */
! 652: }
! 653: nkeys = 0;
! 654: mod = ud->modifiers;
! 655: omod = sc->sc_odata.modifiers;
! 656: if (mod != omod)
! 657: for (i = 0; i < sc->sc_nmod; i++)
! 658: if (( mod & sc->sc_mods[i].mask) !=
! 659: (omod & sc->sc_mods[i].mask))
! 660: ADDKEY(sc->sc_mods[i].key |
! 661: (mod & sc->sc_mods[i].mask
! 662: ? PRESS : RELEASE));
! 663: if (memcmp(ud->keycode, sc->sc_odata.keycode, sc->sc_nkeycode) != 0) {
! 664: /* Check for released keys. */
! 665: for (i = 0; i < sc->sc_nkeycode; i++) {
! 666: key = sc->sc_odata.keycode[i];
! 667: if (key == 0)
! 668: continue;
! 669: for (j = 0; j < sc->sc_nkeycode; j++)
! 670: if (key == ud->keycode[j])
! 671: goto rfound;
! 672: DPRINTFN(3,("ukbd_intr: relse key=0x%02x\n", key));
! 673: ADDKEY(key | RELEASE);
! 674: rfound:
! 675: ;
! 676: }
! 677:
! 678: /* Check for pressed keys. */
! 679: for (i = 0; i < sc->sc_nkeycode; i++) {
! 680: key = ud->keycode[i];
! 681: if (key == 0)
! 682: continue;
! 683: for (j = 0; j < sc->sc_nkeycode; j++)
! 684: if (key == sc->sc_odata.keycode[j])
! 685: goto pfound;
! 686: DPRINTFN(2,("ukbd_intr: press key=0x%02x\n", key));
! 687: ADDKEY(key | PRESS);
! 688: pfound:
! 689: ;
! 690: }
! 691: }
! 692: sc->sc_odata = *ud;
! 693:
! 694: if (nkeys == 0)
! 695: return;
! 696:
! 697: if (sc->sc_polling) {
! 698: DPRINTFN(1,("ukbd_intr: pollchar = 0x%03x\n", ibuf[0]));
! 699: memcpy(sc->sc_pollchars, ibuf, nkeys * sizeof(u_int16_t));
! 700: sc->sc_npollchar = nkeys;
! 701: return;
! 702: }
! 703: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 704: if (sc->sc_rawkbd) {
! 705: u_char cbuf[MAXKEYS * 2];
! 706: int c;
! 707: int npress;
! 708:
! 709: for (npress = i = j = 0; i < nkeys; i++) {
! 710: key = ibuf[i];
! 711: c = ukbd_trtab[key & CODEMASK];
! 712: if (c == NN)
! 713: continue;
! 714: if (c & 0x80)
! 715: cbuf[j++] = 0xe0;
! 716: cbuf[j] = c & 0x7f;
! 717: if (key & RELEASE)
! 718: cbuf[j] |= 0x80;
! 719: else {
! 720: /* remember pressed keys for autorepeat */
! 721: if (c & 0x80)
! 722: sc->sc_rep[npress++] = 0xe0;
! 723: sc->sc_rep[npress++] = c & 0x7f;
! 724: }
! 725: DPRINTFN(1,("ukbd_intr: raw = %s0x%02x\n",
! 726: c & 0x80 ? "0xe0 " : "",
! 727: cbuf[j]));
! 728: j++;
! 729: }
! 730: s = spltty();
! 731: wskbd_rawinput(sc->sc_wskbddev, cbuf, j);
! 732: splx(s);
! 733: timeout_del(&sc->sc_rawrepeat_ch);
! 734: if (npress != 0) {
! 735: sc->sc_nrep = npress;
! 736: timeout_del(&sc->sc_rawrepeat_ch);
! 737: timeout_set(&sc->sc_rawrepeat_ch, ukbd_rawrepeat, sc);
! 738: timeout_add(&sc->sc_rawrepeat_ch,
! 739: hz * REP_DELAY1 / 1000);
! 740: }
! 741: return;
! 742: }
! 743: #endif
! 744:
! 745: s = spltty();
! 746: for (i = 0; i < nkeys; i++) {
! 747: key = ibuf[i];
! 748: wskbd_input(sc->sc_wskbddev,
! 749: key&RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN,
! 750: key&CODEMASK);
! 751: }
! 752: splx(s);
! 753: }
! 754:
! 755: void
! 756: ukbd_set_leds(void *v, int leds)
! 757: {
! 758: struct ukbd_softc *sc = v;
! 759: u_int8_t res;
! 760:
! 761: DPRINTF(("ukbd_set_leds: sc=%p leds=%d, sc_leds=%d\n",
! 762: sc, leds, sc->sc_leds));
! 763:
! 764: if (sc->sc_dying)
! 765: return;
! 766:
! 767: if (sc->sc_leds == leds)
! 768: return;
! 769: sc->sc_leds = leds;
! 770: res = 0;
! 771: /* XXX not really right */
! 772: if ((leds & WSKBD_LED_SCROLL) && sc->sc_scroloc.size == 1)
! 773: res |= 1 << sc->sc_scroloc.pos;
! 774: if ((leds & WSKBD_LED_NUM) && sc->sc_numloc.size == 1)
! 775: res |= 1 << sc->sc_numloc.pos;
! 776: if ((leds & WSKBD_LED_CAPS) && sc->sc_capsloc.size == 1)
! 777: res |= 1 << sc->sc_capsloc.pos;
! 778: uhidev_set_report_async(&sc->sc_hdev, UHID_OUTPUT_REPORT, &res, 1);
! 779: }
! 780:
! 781: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 782: void
! 783: ukbd_rawrepeat(void *v)
! 784: {
! 785: struct ukbd_softc *sc = v;
! 786: int s;
! 787:
! 788: s = spltty();
! 789: wskbd_rawinput(sc->sc_wskbddev, sc->sc_rep, sc->sc_nrep);
! 790: splx(s);
! 791: timeout_del(&sc->sc_rawrepeat_ch);
! 792: timeout_set(&sc->sc_rawrepeat_ch, ukbd_rawrepeat, sc);
! 793: timeout_add(&sc->sc_rawrepeat_ch, hz * REP_DELAYN / 1000);
! 794: }
! 795: #endif
! 796:
! 797: int
! 798: ukbd_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
! 799: {
! 800: struct ukbd_softc *sc = v;
! 801:
! 802: switch (cmd) {
! 803: case WSKBDIO_GTYPE:
! 804: *(int *)data = WSKBD_TYPE_USB;
! 805: return (0);
! 806: case WSKBDIO_SETLEDS:
! 807: ukbd_set_leds(v, *(int *)data);
! 808: return (0);
! 809: case WSKBDIO_GETLEDS:
! 810: *(int *)data = sc->sc_leds;
! 811: return (0);
! 812: #ifdef WSDISPLAY_COMPAT_RAWKBD
! 813: case WSKBDIO_SETMODE:
! 814: DPRINTF(("ukbd_ioctl: set raw = %d\n", *(int *)data));
! 815: sc->sc_rawkbd = *(int *)data == WSKBD_RAW;
! 816: timeout_del(&sc->sc_rawrepeat_ch);
! 817: return (0);
! 818: #endif
! 819: }
! 820: return (-1);
! 821: }
! 822:
! 823: /*
! 824: * This is a hack to work around some broken ports that don't call
! 825: * cnpollc() before cngetc().
! 826: */
! 827: static int pollenter, warned;
! 828:
! 829: /* Console interface. */
! 830: void
! 831: ukbd_cngetc(void *v, u_int *type, int *data)
! 832: {
! 833: struct ukbd_softc *sc = v;
! 834: int c;
! 835: int broken;
! 836:
! 837: if (pollenter == 0) {
! 838: if (!warned) {
! 839: printf("\n"
! 840: "This port is broken, it does not call cnpollc() before calling cngetc().\n"
! 841: "This should be fixed, but it will work anyway (for now).\n");
! 842: warned = 1;
! 843: }
! 844: broken = 1;
! 845: ukbd_cnpollc(v, 1);
! 846: } else
! 847: broken = 0;
! 848:
! 849: DPRINTFN(0,("ukbd_cngetc: enter\n"));
! 850: sc->sc_polling = 1;
! 851: while(sc->sc_npollchar <= 0)
! 852: usbd_dopoll(sc->sc_hdev.sc_parent->sc_iface);
! 853: sc->sc_polling = 0;
! 854: c = sc->sc_pollchars[0];
! 855: sc->sc_npollchar--;
! 856: memcpy(sc->sc_pollchars, sc->sc_pollchars+1,
! 857: sc->sc_npollchar * sizeof(u_int16_t));
! 858: *type = c & RELEASE ? WSCONS_EVENT_KEY_UP : WSCONS_EVENT_KEY_DOWN;
! 859: *data = c & CODEMASK;
! 860: DPRINTFN(0,("ukbd_cngetc: return 0x%02x\n", c));
! 861: if (broken)
! 862: ukbd_cnpollc(v, 0);
! 863: }
! 864:
! 865: void
! 866: ukbd_cnpollc(void *v, int on)
! 867: {
! 868: struct ukbd_softc *sc = v;
! 869: usbd_device_handle dev;
! 870:
! 871: DPRINTFN(2,("ukbd_cnpollc: sc=%p on=%d\n", v, on));
! 872:
! 873: usbd_interface2device_handle(sc->sc_hdev.sc_parent->sc_iface, &dev);
! 874: if (on) {
! 875: sc->sc_spl = splusb();
! 876: pollenter++;
! 877: } else {
! 878: splx(sc->sc_spl);
! 879: pollenter--;
! 880: }
! 881: usbd_set_polling(dev, on);
! 882: }
! 883:
! 884: int
! 885: ukbd_cnattach(void)
! 886: {
! 887:
! 888: /*
! 889: * XXX USB requires too many parts of the kernel to be running
! 890: * XXX in order to work, so we can't do much for the console
! 891: * XXX keyboard until autconfiguration has run its course.
! 892: */
! 893: ukbd_is_console = 1;
! 894: return (0);
! 895: }
! 896:
! 897: const char *
! 898: ukbd_parse_desc(struct ukbd_softc *sc)
! 899: {
! 900: struct hid_data *d;
! 901: struct hid_item h;
! 902: int size;
! 903: void *desc;
! 904: int imod;
! 905:
! 906: uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size);
! 907: imod = 0;
! 908: sc->sc_nkeycode = 0;
! 909: d = hid_start_parse(desc, size, hid_input);
! 910: while (hid_get_item(d, &h)) {
! 911: /*printf("ukbd: id=%d kind=%d usage=0x%x flags=0x%x pos=%d size=%d cnt=%d\n",
! 912: h.report_ID, h.kind, h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count);*/
! 913: if (h.kind != hid_input || (h.flags & HIO_CONST) ||
! 914: HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD ||
! 915: h.report_ID != sc->sc_hdev.sc_report_id)
! 916: continue;
! 917: DPRINTF(("ukbd: imod=%d usage=0x%x flags=0x%x pos=%d size=%d "
! 918: "cnt=%d\n", imod,
! 919: h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count));
! 920: if (h.flags & HIO_VARIABLE) {
! 921: if (h.loc.size != 1)
! 922: return ("bad modifier size");
! 923: /* Single item */
! 924: if (imod < MAXMOD) {
! 925: sc->sc_modloc[imod] = h.loc;
! 926: sc->sc_mods[imod].mask = 1 << imod;
! 927: sc->sc_mods[imod].key = HID_GET_USAGE(h.usage);
! 928: imod++;
! 929: } else
! 930: return ("too many modifier keys");
! 931: } else {
! 932: /* Array */
! 933: if (h.loc.size != 8)
! 934: return ("key code size != 8");
! 935: if (h.loc.count > MAXKEYCODE)
! 936: return ("too many key codes");
! 937: if (h.loc.pos % 8 != 0)
! 938: return ("key codes not on byte boundary");
! 939: if (sc->sc_nkeycode != 0)
! 940: return ("multiple key code arrays\n");
! 941: sc->sc_keycodeloc = h.loc;
! 942: sc->sc_nkeycode = h.loc.count;
! 943: }
! 944: }
! 945: sc->sc_nmod = imod;
! 946: hid_end_parse(d);
! 947:
! 948: hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_NUM_LOCK),
! 949: sc->sc_hdev.sc_report_id, hid_output, &sc->sc_numloc, NULL);
! 950: hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_CAPS_LOCK),
! 951: sc->sc_hdev.sc_report_id, hid_output, &sc->sc_capsloc, NULL);
! 952: hid_locate(desc, size, HID_USAGE2(HUP_LEDS, HUD_LED_SCROLL_LOCK),
! 953: sc->sc_hdev.sc_report_id, hid_output, &sc->sc_scroloc, NULL);
! 954:
! 955: return (NULL);
! 956: }
CVSweb