Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 26 Apr 2008 22:40:00 +0200
From:      Bernhard Schmidt <scb@techwires.net>
To:        freebsd-usb@freebsd.org
Subject:   Macbook Fn key, ukbd
Message-ID:  <48139320.5040905@techwires.net>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------000208050209040703010703
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 7bit

Hi,

The attached patch enables the Fn key on Macbooks which is required to 
get Home, End, [..] keys to work.

Now I need some input on a good way to detect if a keyboard has that Fn 
key. Currently I'm using the quirk approach, possible would also be to 
use hid_locate() as Rui Paulo did in 
http://fnop.net/~rpaulo/priv/freebsd/macbook-aug-1.diff and defining a 
option named UKBD_HAS_FN or something would work too. So what's the way 
to go for?

To all Macbook (Pro) users, if you can afford some spare time and test 
the patch, feedback welcome. :)



Regards,
Bernhard Schmidt

--------------000208050209040703010703
Content-Type: text/plain; x-mac-type="0"; x-mac-creator="0";
	name="macbookfnkey.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="macbookfnkey.diff"

diff -ur /usr/src.orig/sys/dev/usb/ukbd.c /usr/src/sys/dev/usb/ukbd.c
--- /usr/src.orig/sys/dev/usb/ukbd.c	2008-04-26 18:40:35.000000000 +0200
+++ /usr/src/sys/dev/usb/ukbd.c	2008-04-26 18:50:23.000000000 +0200
@@ -110,6 +110,9 @@
 
 typedef struct ukbd_softc {
 	device_t		sc_dev;		/* base device */
+	int 			sc_flags;
+#define UKBD_FNKEY		0x01
+#define UKBD_HASFN		0x01
 } ukbd_softc_t;
 
 #define	UKBD_CHUNK	128	/* chunk size for read */
@@ -191,6 +194,11 @@
 
 	id = usbd_get_interface_descriptor(iface);
 
+	if (usbd_get_quirks(uaa->device)->uq_flags & UQ_APPLE_HAS_FN) {
+		device_printf(self, "Apple Macbook Fn key detected\n.");
+		sc->sc_flags |= UKBD_HASFN;
+	}
+
 	arg[0] = (void *)uaa;
 	arg[1] = (void *)ukbd_intr;
 	kbd = NULL;
@@ -206,6 +214,8 @@
 #endif
 	if (bootverbose)
 		(*sw->diag)(kbd, bootverbose);
+	if (sc->sc_flags & UKBD_HASFN)
+                kbd->kb_flags |= UKBD_HASFN;
 
 	return 0;
 }
@@ -364,6 +374,7 @@
 	int		ks_mode;	/* input mode (K_XLATE,K_RAW,K_CODE) */
 	int		ks_flags;	/* flags */
 #define COMPOSE		(1 << 0)
+#define FNKEY		(2 << 0)
 	int		ks_polling;
 	int		ks_state;	/* shift/lock key state */
 	int		ks_accents;	/* accent key index (> 0) */
@@ -866,6 +877,7 @@
 		c = -1;
 	} else {
 		c = state->ks_input[state->ks_inputhead];
+		DPRINTF(("ukbd_getc: read %04x\n", c));
 		--state->ks_inputs;
 		state->ks_inputhead = (state->ks_inputhead + 1)%INPUTBUFSIZE;
 	}
@@ -1031,6 +1043,53 @@
 		return NOKEY;
 	++kbd->kb_count;
 
+	if (!(kbd->kb_flags & UKBD_HASFN))
+		goto no_fn_key;
+
+	/* fn key pressed */
+	if (usbcode == UKBD_FNKEY) {
+		if (!(state->ks_flags & FNKEY)) {
+			state->ks_flags |= FNKEY;
+			DPRINTFN(1, ("ukbd_read_char: fn key pressed\n"));
+		}
+		--kbd->kb_count;
+		goto next_code;
+	}
+
+	/* fn key released */
+	else if (KEY_INDEX(usbcode) == UKBD_FNKEY) {
+		if (state->ks_flags & FNKEY) {
+			state->ks_flags &= ~FNKEY;
+			DPRINTFN(1, ("ukbd_read_char: fn key released\n"));
+		}
+		--kbd->kb_count;
+		goto next_code;
+	}
+
+	/* translate scancodes while fn key is pressed */
+	else if (state->ks_flags & FNKEY) {
+		DPRINTFN(2, ("ukbd_read_char: translate: %04x\n", usbcode));
+		switch (KEY_INDEX(usbcode)) {
+		case 0x2a:		/* delete */
+			usbcode = 0x4c | (usbcode & KEY_RELEASE ? KEY_RELEASE : 0x0);
+			break;
+		case 0x4f:		/* end */
+			usbcode = 0x4d | (usbcode & KEY_RELEASE ? KEY_RELEASE : 0x0);
+			break;
+		case 0x50:		/* home */
+			usbcode = 0x4a | (usbcode & KEY_RELEASE ? KEY_RELEASE : 0x0);
+			break;
+		case 0x51:		/* pagedown */
+			usbcode = 0x4e | (usbcode & KEY_RELEASE ? KEY_RELEASE : 0x0);
+			break;
+		case 0x52:		/* pageup */
+			usbcode = 0x4b | (usbcode & KEY_RELEASE ? KEY_RELEASE : 0x0);
+			break;
+		}
+		DPRINTFN(2, ("ukbd_read_char: translated to %04x\n", usbcode));
+	}
+no_fn_key:
+
 #ifdef UKBD_EMULATE_ATSCANCODE
 	/* USB key index -> key code -> AT scan code */
 	keycode = ukbd_trtab[KEY_INDEX(usbcode)];
diff -ur /usr/src.orig/sys/dev/usb/usb_quirks.c /usr/src/sys/dev/usb/usb_quirks.c
--- /usr/src.orig/sys/dev/usb/usb_quirks.c	2008-04-26 18:40:35.000000000 +0200
+++ /usr/src/sys/dev/usb/usb_quirks.c	2008-04-26 18:45:03.000000000 +0200
@@ -90,6 +90,8 @@
    ANY, { UQ_MS_BAD_CLASS | UQ_MS_LEADING_BYTE }},
  { USB_VENDOR_MICROSOFT, USB_PRODUCT_MICROSOFT_WLINTELLIMOUSE,
    ANY, { UQ_MS_LEADING_BYTE }},
+/* Apple Macbook Keyboard */
+ { USB_VENDOR_APPLE, USB_PRODUCT_APPLE_INT_KBD,     ANY,   { UQ_APPLE_HAS_FN }},
 
  /* Devices which should be ignored by uhid */
  { USB_VENDOR_APC, USB_PRODUCT_APC_UPS,
diff -ur /usr/src.orig/sys/dev/usb/usb_quirks.h /usr/src/sys/dev/usb/usb_quirks.h
--- /usr/src.orig/sys/dev/usb/usb_quirks.h	2008-04-26 18:40:35.000000000 +0200
+++ /usr/src/sys/dev/usb/usb_quirks.h	2008-04-26 18:45:03.000000000 +0200
@@ -57,6 +57,7 @@
 #define UQ_KBD_IGNORE	0x00018000 /* device should be ignored by both kbd and hid class */
 #define	UQ_MS_BAD_CLASS 0x00020000 /* doesn't identify properly */
 #define	UQ_MS_LEADING_BYTE 0x40000 /* mouse sends an unknown leading byte. */
+#define UQ_APPLE_HAS_FN 0x00080000 /* keyboard has fn key */
 };
 
 extern const struct usbd_quirks usbd_no_quirk;
diff -ur /usr/src.orig/sys/dev/usb/usbdevs /usr/src/sys/dev/usb/usbdevs
--- /usr/src.orig/sys/dev/usb/usbdevs	2008-04-26 18:40:35.000000000 +0200
+++ /usr/src/sys/dev/usb/usbdevs	2008-04-26 18:45:03.000000000 +0200
@@ -838,6 +838,7 @@
 
 /* Apple Computer products */
 product APPLE EXT_KBD		0x020c	Apple Extended USB Keyboard
+product APPLE INT_KBD		0x021b	Apple Macbook Internal Keyboard
 product APPLE OPTMOUSE		0x0302	Optical mouse
 product APPLE MIGHTYMOUSE	0x0304	Mighty Mouse
 product APPLE EXT_KBD_HUB	0x1003	Hub in Apple Extended USB Keyboard

--------------000208050209040703010703--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?48139320.5040905>