Module Name: src Committed By: phx Date: Tue Nov 30 11:35:30 UTC 2010
Modified Files: src/sys/dev/usb: ukbd.c usb_quirks.c usb_quirks.h Log Message: Support for Apple notebook keyboards, which have a few quirks. 1. On ISO-keyboard the keycodes for the key left of '1' and right of Shift are swapped. 2. Find the Apple FN key in the report descriptor and do the translations needed, before passing the keycodes to wscons. 3. Those keyboards only have the left Alt key. AltGr is missing. So it is emulated when holding down FN together with Alt. To generate a diff of this commit: cvs rdiff -u -r1.108 -r1.109 src/sys/dev/usb/ukbd.c cvs rdiff -u -r1.69 -r1.70 src/sys/dev/usb/usb_quirks.c cvs rdiff -u -r1.25 -r1.26 src/sys/dev/usb/usb_quirks.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/usb/ukbd.c diff -u src/sys/dev/usb/ukbd.c:1.108 src/sys/dev/usb/ukbd.c:1.109 --- src/sys/dev/usb/ukbd.c:1.108 Wed Nov 3 22:34:24 2010 +++ src/sys/dev/usb/ukbd.c Tue Nov 30 11:35:30 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: ukbd.c,v 1.108 2010/11/03 22:34:24 dyoung Exp $ */ +/* $NetBSD: ukbd.c,v 1.109 2010/11/30 11:35:30 phx Exp $ */ /* * Copyright (c) 1998 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ukbd.c,v 1.108 2010/11/03 22:34:24 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ukbd.c,v 1.109 2010/11/30 11:35:30 phx Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -92,6 +92,57 @@ #define RELEASE 0x100 #define CODEMASK 0x0ff +struct ukbd_keycodetrans { + u_int8_t from; + u_int8_t to; +}; + +Static const struct ukbd_keycodetrans trtab_apple_fn[] = { + { 0x0c, 0x5d }, /* i -> KP 5 */ + { 0x0d, 0x59 }, /* j -> KP 1 */ + { 0x0e, 0x5a }, /* k -> KP 2 */ + { 0x0f, 0x5b }, /* l -> KP 3 */ + { 0x10, 0x62 }, /* m -> KP 0 */ + { 0x12, 0x5e }, /* o -> KP 6 */ + { 0x13, 0x55 }, /* o -> KP * */ + { 0x18, 0x5c }, /* u -> KP 4 */ + { 0x0c, 0x5d }, /* i -> KP 5 */ + { 0x2a, 0x4c }, /* Backspace -> Delete */ + { 0x28, 0x49 }, /* Return -> Insert */ + { 0x24, 0x5f }, /* 7 -> KP 7 */ + { 0x25, 0x60 }, /* 8 -> KP 8 */ + { 0x26, 0x61 }, /* 9 -> KP 9 */ + { 0x27, 0x54 }, /* 0 -> KP / */ + { 0x2d, 0x67 }, /* - -> KP = */ + { 0x33, 0x56 }, /* ; -> KP - */ + { 0x37, 0x63 }, /* . -> KP . */ + { 0x38, 0x57 }, /* / -> KP + */ + { 0x3a, 0xd1 }, /* F1..F12 mapped to reserved codes 0xd1..0xdc */ + { 0x3b, 0xd2 }, + { 0x3c, 0xd3 }, + { 0x3d, 0xd4 }, + { 0x3e, 0xd5 }, + { 0x3f, 0xd6 }, + { 0x40, 0xd7 }, + { 0x41, 0xd8 }, + { 0x42, 0xd9 }, + { 0x43, 0xda }, + { 0x44, 0xdb }, + { 0x45, 0xdc }, + { 0x4f, 0x4d }, /* Right -> End */ + { 0x50, 0x4a }, /* Left -> Home */ + { 0x51, 0x4e }, /* Down -> PageDown */ + { 0x52, 0x4b }, /* Up -> PageUp */ + { 0x00, 0x00 } +}; + +Static const struct ukbd_keycodetrans trtab_apple_iso[] = { + { 0x35, 0x64 }, /* swap the key above tab with key right of shift */ + { 0x64, 0x35 }, + { 0x31, 0x32 }, /* key left of return is Europe1, not "\|" */ + { 0x00, 0x00 } +}; + #if defined(__NetBSD__) && defined(WSDISPLAY_COMPAT_RAWKBD) #define NN 0 /* no translation */ /* @@ -160,14 +211,21 @@ struct hid_location sc_keycodeloc; u_int sc_nkeycode; - char sc_enabled; + u_int sc_flags; /* flags */ +#define FLAG_ENABLED 0x0001 +#define FLAG_POLLING 0x0002 +#define FLAG_DEBOUNCE 0x0004 /* for quirk handling */ +#define FLAG_APPLE_FIX_ISO 0x0008 +#define FLAG_APPLE_FN 0x0010 +#define FLAG_FN_PRESSED 0x0100 /* FN key is held down */ +#define FLAG_FN_ALT 0x0200 /* Last Alt key was FN-Alt = AltGr */ int sc_console_keyboard; /* we are the console keyboard */ - char sc_debounce; /* for quirk handling */ - struct callout sc_delay; /* for quirk handling */ + struct callout sc_delay; /* for quirk handling */ struct ukbd_data sc_data; /* for quirk handling */ + struct hid_location sc_apple_fn; struct hid_location sc_numloc; struct hid_location sc_capsloc; struct hid_location sc_scroloc; @@ -187,7 +245,6 @@ #endif /* defined(WSDISPLAY_COMPAT_RAWKBD) */ int sc_spl; - int sc_polling; int sc_npollchar; u_int16_t sc_pollchars[MAXKEYS]; #endif /* defined(__NetBSD__) */ @@ -317,6 +374,7 @@ sc->sc_hdev.sc_intr = ukbd_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_report_id = uha->reportid; + sc->sc_flags = 0; if (!pmf_device_register(self, NULL, NULL)) { aprint_normal("\n"); @@ -330,15 +388,23 @@ return; } + /* Quirks */ + qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; + if (qflags & UQ_SPUR_BUT_UP) + sc->sc_flags |= FLAG_DEBOUNCE; + if (qflags & UQ_APPLE_ISO) + sc->sc_flags |= FLAG_APPLE_FIX_ISO; + #ifdef DIAGNOSTIC aprint_normal(": %d modifier keys, %d key codes", sc->sc_nmod, sc->sc_nkeycode); + if (sc->sc_flags & FLAG_APPLE_FN) + aprint_normal(", apple fn key"); + if (sc->sc_flags & FLAG_APPLE_FIX_ISO) + aprint_normal(", fix apple iso"); #endif aprint_normal("\n"); - qflags = usbd_get_quirks(uha->parent->sc_udev)->uq_flags; - sc->sc_debounce = (qflags & UQ_SPUR_BUT_UP) != 0; - /* * Remember if we're the console keyboard. * @@ -388,7 +454,7 @@ return (EIO); /* Should only be called to change state */ - if (sc->sc_enabled == on) { + if ((sc->sc_flags & FLAG_ENABLED) != 0 && on != 0) { #ifdef DIAGNOSTIC printf("ukbd_enable: %s: bad call on=%d\n", device_xname(sc->sc_hdev.sc_dev), on); @@ -397,10 +463,11 @@ } DPRINTF(("ukbd_enable: sc=%p on=%d\n", sc, on)); - sc->sc_enabled = on; if (on) { + sc->sc_flags |= FLAG_ENABLED; return (uhidev_open(&sc->sc_hdev)); } else { + sc->sc_flags &= ~FLAG_ENABLED; uhidev_close(&sc->sc_hdev); return (0); } @@ -476,6 +543,46 @@ return (rv); } +static void +ukbd_translate_keycodes(struct ukbd_softc *sc, struct ukbd_data *ud, + const struct ukbd_keycodetrans *tab) +{ + const struct ukbd_keycodetrans *tp; + int i; + u_int8_t key; + + for (i = 0; i < sc->sc_nkeycode; i++) { + key = ud->keycode[i]; + if (key) + for (tp = tab; tp->from; tp++) + if (tp->from == key) { + ud->keycode[i] = tp->to; + break; + } + } +} + +static u_int16_t +ukbd_translate_modifier(struct ukbd_softc *sc, u_int16_t key) +{ + if ((sc->sc_flags & FLAG_APPLE_FN) && (key & CODEMASK) == 0x00e2) { + if ((key & ~CODEMASK) == PRESS) { + if (sc->sc_flags & FLAG_FN_PRESSED) { + /* pressed FN-Alt, translate to AltGr */ + key = 0x00e6 | PRESS; + sc->sc_flags |= FLAG_FN_ALT; + } + } else { + if (sc->sc_flags & FLAG_FN_ALT) { + /* released Alt, which was treated as FN-Alt */ + key = 0x00e6 | RELEASE; + sc->sc_flags &= ~FLAG_FN_ALT; + } + } + } + return key; +} + void ukbd_intr(struct uhidev *addr, void *ibuf, u_int len) { @@ -499,7 +606,16 @@ memcpy(ud->keycode, (char *)ibuf + sc->sc_keycodeloc.pos / 8, sc->sc_nkeycode); - if (sc->sc_debounce && !sc->sc_polling) { + if (sc->sc_flags & FLAG_APPLE_FN) { + if (hid_get_data(ibuf, &sc->sc_apple_fn)) { + sc->sc_flags |= FLAG_FN_PRESSED; + ukbd_translate_keycodes(sc, ud, trtab_apple_fn); + } + else + sc->sc_flags &= ~FLAG_FN_PRESSED; + } + + if ((sc->sc_flags & FLAG_DEBOUNCE) && !(sc->sc_flags & FLAG_POLLING)) { /* * Some keyboards have a peculiar quirk. They sometimes * generate a key up followed by a key down for the same @@ -509,7 +625,7 @@ sc->sc_data = *ud; callout_reset(&sc->sc_delay, hz / 50, ukbd_delayed_decode, sc); #ifdef DDB - } else if (sc->sc_console_keyboard && !sc->sc_polling) { + } else if (sc->sc_console_keyboard && !(sc->sc_flags & FLAG_POLLING)) { /* * For the console keyboard we can't deliver CTL-ALT-ESC * from the interrupt routine. Doing so would start @@ -570,16 +686,22 @@ DPRINTF(("ukbd_intr: KEY_ERROR\n")); return; /* ignore */ } + + if (sc->sc_flags & FLAG_APPLE_FIX_ISO) + ukbd_translate_keycodes(sc, ud, trtab_apple_iso); + nkeys = 0; mod = ud->modifiers; omod = sc->sc_odata.modifiers; if (mod != omod) for (i = 0; i < sc->sc_nmod; i++) if (( mod & sc->sc_mods[i].mask) != - (omod & sc->sc_mods[i].mask)) - ADDKEY(sc->sc_mods[i].key | - (mod & sc->sc_mods[i].mask - ? PRESS : RELEASE)); + (omod & sc->sc_mods[i].mask)) { + key = sc->sc_mods[i].key | + ((mod & sc->sc_mods[i].mask) ? + PRESS : RELEASE); + ADDKEY(ukbd_translate_modifier(sc, key)); + } if (memcmp(ud->keycode, sc->sc_odata.keycode, sc->sc_nkeycode) != 0) { /* Check for released keys. */ for (i = 0; i < sc->sc_nkeycode; i++) { @@ -614,7 +736,7 @@ if (nkeys == 0) return; - if (sc->sc_polling) { + if (sc->sc_flags & FLAG_POLLING) { DPRINTFN(1,("ukbd_intr: pollchar = 0x%03x\n", ibuf[0])); memcpy(sc->sc_pollchars, ibuf, nkeys * sizeof(u_int16_t)); sc->sc_npollchar = nkeys; @@ -779,10 +901,10 @@ broken = 0; DPRINTFN(0,("ukbd_cngetc: enter\n")); - sc->sc_polling = 1; + sc->sc_flags |= FLAG_POLLING; while(sc->sc_npollchar <= 0) usbd_dopoll(sc->sc_hdev.sc_parent->sc_iface); - sc->sc_polling = 0; + sc->sc_flags &= ~FLAG_POLLING; c = sc->sc_pollchars[0]; sc->sc_npollchar--; memcpy(sc->sc_pollchars, sc->sc_pollchars+1, @@ -842,6 +964,15 @@ while (hid_get_item(d, &h)) { /*printf("ukbd: id=%d kind=%d usage=0x%x flags=0x%x pos=%d size=%d cnt=%d\n", h.report_ID, h.kind, h.usage, h.flags, h.loc.pos, h.loc.size, h.loc.count);*/ + + /* Check for special Apple notebook FN key */ + if (HID_GET_USAGE_PAGE(h.usage) == 0x00ff && + HID_GET_USAGE(h.usage) == 0x0003 && + h.kind == hid_input && (h.flags & HIO_VARIABLE)) { + sc->sc_flags |= FLAG_APPLE_FN; + sc->sc_apple_fn = h.loc; + } + if (h.kind != hid_input || (h.flags & HIO_CONST) || HID_GET_USAGE_PAGE(h.usage) != HUP_KEYBOARD || h.report_ID != sc->sc_hdev.sc_report_id) Index: src/sys/dev/usb/usb_quirks.c diff -u src/sys/dev/usb/usb_quirks.c:1.69 src/sys/dev/usb/usb_quirks.c:1.70 --- src/sys/dev/usb/usb_quirks.c:1.69 Wed Nov 3 22:34:24 2010 +++ src/sys/dev/usb/usb_quirks.c Tue Nov 30 11:35:30 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: usb_quirks.c,v 1.69 2010/11/03 22:34:24 dyoung Exp $ */ +/* $NetBSD: usb_quirks.c,v 1.70 2010/11/30 11:35:30 phx Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_quirks.c,v 1.30 2003/01/02 04:15:55 imp Exp $ */ /* @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: usb_quirks.c,v 1.69 2010/11/03 22:34:24 dyoung Exp $"); +__KERNEL_RCSID(0, "$NetBSD: usb_quirks.c,v 1.70 2010/11/30 11:35:30 phx Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -56,6 +56,7 @@ } usb_quirks[] = { /* Devices which should be ignored by uhid */ { USB_VENDOR_APC, USB_PRODUCT_APC_UPS, ANY, { UQ_HID_IGNORE }}, + { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_ADB, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_CYBERPOWER, USB_PRODUCT_CYBERPOWER_UPS, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS1, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_MGE, USB_PRODUCT_MGE_UPS2, ANY, { UQ_HID_IGNORE }}, @@ -63,7 +64,6 @@ ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_TRIPPLITE2, USB_PRODUCT_TRIPPLITE2_UPS, ANY, { UQ_HID_IGNORE }}, - { USB_VENDOR_METAGEEK, USB_PRODUCT_METAGEEK_WISPY_24X, ANY, { UQ_HID_IGNORE }}, { USB_VENDOR_KYE, USB_PRODUCT_KYE_NICHE, 0x100, { UQ_NO_SET_PROTO}}, @@ -119,6 +119,12 @@ { USB_VENDOR_HP, USB_PRODUCT_HP_1220C, ANY, { UQ_BROKEN_BIDIR }}, + /* Apple internal notebook ISO keyboards have swapped keys */ + { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_FOUNTAIN_ISO, + ANY, { UQ_APPLE_ISO }}, + { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_GEYSER_ISO, + ANY, { UQ_APPLE_ISO }}, + /* HID and audio are both invalid on iPhone/iPod Touch */ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_IPHONE, ANY, { UQ_HID_IGNORE | UQ_BAD_AUDIO }}, Index: src/sys/dev/usb/usb_quirks.h diff -u src/sys/dev/usb/usb_quirks.h:1.25 src/sys/dev/usb/usb_quirks.h:1.26 --- src/sys/dev/usb/usb_quirks.h:1.25 Fri Oct 1 20:56:10 2010 +++ src/sys/dev/usb/usb_quirks.h Tue Nov 30 11:35:30 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: usb_quirks.h,v 1.25 2010/10/01 20:56:10 christos Exp $ */ +/* $NetBSD: usb_quirks.h,v 1.26 2010/11/30 11:35:30 phx Exp $ */ /* $FreeBSD: src/sys/dev/usb/usb_quirks.h,v 1.9 1999/11/12 23:31:03 n_hibma Exp $ */ /* @@ -48,6 +48,7 @@ #define UQ_HID_IGNORE 0x4000 /* device should be ignored by hid class */ #define UQ_NO_UNION_NRM 0x8000 /* has no normal UNION descriptor */ #define UQ_LOST_CS_DESC 0x10000 /* look everywhere for the CS descriptors */ +#define UQ_APPLE_ISO 0x20000 /* force ISO layout on Apple keyboards */ }; extern const struct usbd_quirks usbd_no_quirk;