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__

Reply via email to