How to add support for Macbook Pro (USB) keyboard?

Christoph Langguth christoph at rosenkeller.org
Sat May 16 15:27:00 UTC 2009


Hi Hans Petter,

thanks for the quick reply!
I tried your patch but it doesn't work, all I get is:

May 16 14:00:01 marvin kernel: ukbd0: <Apple Internal Keyboard> on usbus5
May 16 14:00:01 marvin kernel: device_attach: ukbd0 attach returned 6

I don't know why this happens... I tried, but unfortunately I didn't 
quite understand what the patch is supposed to do :-)


In the meantime, I had continued writing up a slightly cleaner patch, 
that also allows to fully utilize the keyboard. Some considerations first:

- Quite a few apple keyboards (mine included) have an additional quirk 
in that they switch two keys. (On [1], that's all the ones having the 
APPLE_ISO_KEYBOARD flag). But of course, it's not all of them :-(

- To fully make use of the keyboard, one also needs to consider the Fn 
key ("part of" the 10th (9th payload) byte), so as to be able to be able 
to type "vital" keys like DEL, INS, PGUP etc. Judging from [1], 
everything from Apple that actually is a keyboard also has this FN key, 
so I would expect all of these keyboards to send that 9th byte

- It seems that some, but not all, of those keyboards, send this first 
byte (the one you called hid_id)


In light of the above, would it make sense to
- try to detect the HID ID byte in the manner that you suggested in your 
patch (assuming that I can figure out the logic, and what's going wrong 
there, I think it's the cleanest and vendor-independent solution)
- add an additional UKBD-specific quirk for the ISO_KEYBOARD problem 
(into usb/quirks/...)
- assume that any available 9th byte contains the Fn key information -- 
or would yet another quirk, listing all affected products, be more reliable?

[1]: https://www.linuxhq.com/kernel/v2.6/28-rc8/drivers/hid/hid-apple.c

In any case, I have attached the "full" patch that works for me -- as 
stated above, it's not as clean as it should be because it only works 
for my specific piece of hardware and doesn't consider the latest 
questions/proposed solution... but it already contains all the required 
logic so that you can see what I meant.

Again, thanks for your help!
Cheers
Chris

Hans Petter Selasky wrote:
> On Friday 15 May 2009, Christoph Langguth wrote:
>> Christoph Langguth
> 
> Hi,
> 
> Can you try the following patch on 8-current?
> 
> http://perforce.freebsd.org/chv.cgi?CH=162145
> 
> --HPS
> 

-------------- next part --------------
--- ukbd.c.org	2009-05-16 13:49:48.000000000 +0000
+++ ukbd.c	2009-05-16 14:39:35.000000000 +0000
@@ -162,6 +162,10 @@
 	uint8_t	sc_leds;		/* store for async led requests */
 	uint8_t	sc_iface_index;
 	uint8_t	sc_iface_no;
+
+	uint8_t quirks;
+#define UKBD_QUIRK_APPLE 1
+#define UKBD_QUIRK_SWAPKEYS 2
 };
 
 #define	KEY_ERROR	  0x01
@@ -464,23 +468,63 @@
 	}
 }
 
+static uint8_t
+apple_fn(uint8_t keycode) {
+	switch (keycode) {
+		case 0x2a: return 0x4c; // BACKSPACE -> DEL
+		case 0x2c: return 0x49; // SPACE -> INSERT
+		case 0x50: return 0x4a; // LEFT ARROW -> HOME
+		case 0x4f: return 0x4d; // RIGHT ARROW -> END
+		case 0x52: return 0x4b; // UP ARROW -> PGUP
+		case 0x51: return 0x4e; // DOWN ARROW -> PGDN
+		default: return keycode;
+	}
+}
+
 static void
 ukbd_intr_callback(struct usb2_xfer *xfer)
 {
 	struct ukbd_softc *sc = xfer->priv_sc;
 	uint16_t len = xfer->actlen;
 	uint8_t i;
+	uint8_t offset = 0;
+	uint8_t fnmode = 0;
+#define UKBD_FNMODE_FN 2
+#define UKBD_FNMODE_EJECT 1
 
 	switch (USB_GET_STATE(xfer)) {
 	case USB_ST_TRANSFERRED:
 		DPRINTF("actlen=%d bytes\n", len);
 
 		if (len > sizeof(sc->sc_ndata)) {
+			if (sc->quirks & UKBD_QUIRK_APPLE) {
+				offset = 1;
+				if (len >= 10) {
+					usb2_copy_out(xfer->frbuffers, 9, &fnmode, 1);
+				}
+			}
 			len = sizeof(sc->sc_ndata);
 		}
 		if (len) {
 			bzero(&sc->sc_ndata, sizeof(sc->sc_ndata));
-			usb2_copy_out(xfer->frbuffers, 0, &sc->sc_ndata, len);
+			usb2_copy_out(xfer->frbuffers, offset, &sc->sc_ndata, len);
+
+			if (sc->quirks & UKBD_QUIRK_APPLE) {
+				for (i = 0; i < UKBD_NKEYCODE; i++) {
+					if ((sc->quirks & UKBD_QUIRK_SWAPKEYS)
+					    && (sc->sc_ndata.keycode[i] == 0x35)) {
+						sc->sc_ndata.keycode[i] = 0x64;
+					}
+					else if ((sc->quirks & UKBD_QUIRK_SWAPKEYS)
+					    && (sc->sc_ndata.keycode[i] == 0x64)) {
+						sc->sc_ndata.keycode[i] = 0x35;
+					}
+					if (fnmode & UKBD_FNMODE_FN) {
+						sc->sc_ndata.keycode[i] = apple_fn(sc->sc_ndata.keycode[i]);
+					}
+				}
+			}
+
 #if USB_DEBUG
 			if (sc->sc_ndata.modifiers) {
 				DPRINTF("mod: 0x%04x\n", sc->sc_ndata.modifiers);
@@ -638,6 +682,12 @@
 	sc->sc_mode = K_XLATE;
 	sc->sc_iface = uaa->iface;
 
+	if ((uaa->info.idVendor == 0x5ac)
+	    && (uaa->info.idProduct == 0x231)) {
+		DPRINTF("Activating Apple kbd quirk\n");
+		sc->quirks = UKBD_QUIRK_APPLE | UKBD_QUIRK_SWAPKEYS;
+	}
+
 	usb2_callout_init_mtx(&sc->sc_callout, &Giant, 0);
 
 	err = usb2_transfer_setup(uaa->device,


More information about the freebsd-usb mailing list