this makes the extra keys on most apple macbooks and apple usb/bluetooth-via-usb keyboards work like they do in mac os. most importantly, fn+up arrow does page up, fn+down does page down, fn+left does home, fn+right does end, fn+backspace does delete, and fn+return does insert. it also makes fn+f10 mute, fn+f11 lower the volume, and fn+f12 raise the volume through the wscons/audio stuff already in place.
i've only tested it on a 1st gen macbook air and on a generic usb keyboard that didn't break. some of this code comes from freebsd and netbsd. please test on usb-connected keyboards, especially on any apples. Index: dev/usb/ukbd.c =================================================================== RCS file: /cvs/src/sys/dev/usb/ukbd.c,v retrieving revision 1.55 diff -u -p -u -p -r1.55 ukbd.c --- dev/usb/ukbd.c 3 Jul 2011 15:47:17 -0000 1.55 +++ dev/usb/ukbd.c 11 Jul 2011 03:45:29 -0000 @@ -132,6 +132,9 @@ struct ukbd_softc { u_char sc_dying; + int sc_apple_fn; + struct hid_location sc_apple_fn_loc; + void (*sc_munge)(void *, uint8_t *, u_int); }; @@ -182,6 +185,7 @@ struct ukbd_translation { #ifdef __loongson__ void ukbd_gdium_munge(void *, uint8_t *, u_int); #endif +void ukbd_apple_munge(void *, uint8_t *, u_int); uint8_t ukbd_translate(const struct ukbd_translation *, size_t, uint8_t); int @@ -207,15 +211,18 @@ ukbd_attach(struct device *parent, struc struct hidkbd *kbd = &sc->sc_kbd; struct usb_attach_arg *uaa = aux; struct uhidev_attach_arg *uha = (struct uhidev_attach_arg *)uaa; + struct hid_data *d; + struct hid_item h; usb_hid_descriptor_t *hid; u_int32_t qflags; - int dlen, repid; + int dlen, repid, size; void *desc; kbd_t layout = (kbd_t)-1; sc->sc_hdev.sc_intr = ukbd_intr; sc->sc_hdev.sc_parent = uha->parent; sc->sc_hdev.sc_report_id = uha->reportid; + sc->sc_apple_fn = 0; uhidev_get_report_desc(uha->parent, &desc, &dlen); repid = uha->reportid; @@ -227,6 +234,25 @@ ukbd_attach(struct device *parent, struc if (hidkbd_attach(self, kbd, 1, qflags, repid, desc, dlen) != 0) return; + /* check for apple keyboards that need Fn-key help */ + if (uha->uaa->vendor == USB_VENDOR_APPLE) { + uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); + d = hid_start_parse(desc, size, hid_input); + while (hid_get_item(d, &h)) { + if (HID_GET_USAGE_PAGE(h.usage) == 0x00ff && + HID_GET_USAGE(h.usage) == 0x0003 && + h.kind == hid_input && (h.flags & HIO_VARIABLE)) { + sc->sc_apple_fn = 1; + sc->sc_apple_fn_loc = h.loc; + sc->sc_munge = ukbd_apple_munge; +#ifdef DIAGNOSTIC + printf(", apple Fn-key help\n"); +#endif + } + } + hid_end_parse(d); + } + if (uha->uaa->vendor == USB_VENDOR_TOPRE && uha->uaa->product == USB_PRODUCT_TOPRE_HHKB) { /* ignore country code on purpose */ @@ -437,6 +463,50 @@ ukbd_translate(const struct ukbd_transla if (table->original == keycode) return table->translation; return 0; +} + +void +ukbd_apple_munge(void *vsc, uint8_t *ibuf, u_int ilen) +{ + struct ukbd_softc *sc = vsc; + struct hidkbd *kbd = &sc->sc_kbd; + uint8_t *pos, *spos, *epos, xlat; + + static const struct ukbd_translation apple_fn_trans[] = { + { 40, 73 }, /* return -> insert */ + { 42, 76 }, /* backspace -> delete */ +#ifdef notyet + { 58, 0 }, /* F1 -> screen brightness down */ + { 59, 0 }, /* F2 -> screen brightness up */ + { 60, 0 }, /* F3 */ + { 61, 0 }, /* F4 */ + { 62, 0 }, /* F5 -> keyboard backlight down */ + { 63, 0 }, /* F6 -> keyboard backlight up */ + { 64, 0 }, /* F7 -> audio back */ + { 65, 0 }, /* F8 -> audio pause/play */ + { 66, 0 }, /* F9 -> audio next */ +#endif + { 67, 127 }, /* F10 -> audio mute */ + { 68, 128 }, /* F11 -> audio volume down */ + { 69, 129 }, /* F12 -> audio volume up */ + { 79, 77 }, /* right -> end */ + { 80, 74 }, /* left -> home */ + { 81, 78 }, /* down -> page down */ + { 82, 75 } /* up -> page up */ + }; + + if (!hid_get_data(ibuf, &sc->sc_apple_fn_loc)) + return; + + spos = ibuf + kbd->sc_keycodeloc.pos / 8; + epos = spos + kbd->sc_nkeycode; + + for (pos = spos; pos != epos; pos++) { + xlat = ukbd_translate(apple_fn_trans, + nitems(apple_fn_trans), *pos); + if (xlat != 0) + *pos = xlat; + } } #ifdef __loongson__