linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
@ 2005-12-25 21:20 Michael Hanselmann
  2005-12-25 21:57 ` Benjamin Herrenschmidt
  2005-12-26  4:04 ` Dmitry Torokhov
  0 siblings, 2 replies; 46+ messages in thread
From: Michael Hanselmann @ 2005-12-25 21:20 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-input, linuxppc-dev, benh, linux-kernel

This patch adds a basic input hook support to usbhid because the quirks
method is outrunning the available bits. A hook for the Fn and Numlock
keys on Apple PowerBooks is included.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>

---
diff -rNup linux-2.6.15-rc6.orig/drivers/usb/input/Kconfig linux-2.6.15-rc6/drivers/usb/input/Kconfig
--- linux-2.6.15-rc6.orig/drivers/usb/input/Kconfig	2005-12-22 20:51:57.000000000 +0100
+++ linux-2.6.15-rc6/drivers/usb/input/Kconfig	2005-12-22 22:01:09.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL
diff -rNup linux-2.6.15-rc6.orig/drivers/usb/input/Makefile linux-2.6.15-rc6/drivers/usb/input/Makefile
--- linux-2.6.15-rc6.orig/drivers/usb/input/Makefile	2005-12-22 20:51:57.000000000 +0100
+++ linux-2.6.15-rc6/drivers/usb/input/Makefile	2005-12-22 23:16:54.000000000 +0100
@@ -25,6 +25,9 @@ endif
 ifeq ($(CONFIG_HID_FF),y)
 	usbhid-objs	+= hid-ff.o
 endif
+ifeq ($(CONFIG_USB_HIDINPUT_POWERBOOK),y)
+   usbhid-objs += hid-input-powerbook.o
+endif
 
 obj-$(CONFIG_USB_AIPTEK)	+= aiptek.o
 obj-$(CONFIG_USB_ATI_REMOTE)	+= ati_remote.o
diff -rNup linux-2.6.15-rc6.orig/drivers/usb/input/hid-input-powerbook.c linux-2.6.15-rc6/drivers/usb/input/hid-input-powerbook.c
--- linux-2.6.15-rc6.orig/drivers/usb/input/hid-input-powerbook.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.6.15-rc6/drivers/usb/input/hid-input-powerbook.c	2005-12-25 21:23:04.000000000 +0100
@@ -0,0 +1,226 @@
+/*
+ * Mapping for special keys on Apple iBook and PowerBook keyboards
+ *
+ * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include "hid.h"
+
+#define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
+#define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
+
+#define APPLE_VENDOR_ID	0x05AC
+#define PB_ENABLED_FN	0x0001
+#define PB_FLAG_FKEY	0x0001
+
+static struct hid_input_hook_device input_pb_devices[] = {
+	{ APPLE_VENDOR_ID, 0x020E },
+	{ APPLE_VENDOR_ID, 0x020F },
+	{ APPLE_VENDOR_ID, 0x0214 },
+	{ APPLE_VENDOR_ID, 0x0215 },
+	{ APPLE_VENDOR_ID, 0x0216 },
+	{ }
+};
+
+struct input_pb_data {
+	u8 enabled_keys;
+};
+
+struct input_pb_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+static struct input_pb_translation fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	PB_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	PB_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		PB_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN, 	PB_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		PB_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		PB_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	PB_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	PB_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	PB_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		PB_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct input_pb_translation numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_ENTER,	KEY_KPENTER },
+	{ }
+};
+
+static int fkeysfirst = 1;
+module_param_named(pb_fkeysfirst, fkeysfirst, bool, 0644);
+MODULE_PARM_DESC(fkeysfirst, "Use fn special keys only while pressing fn");
+
+static int enablefnkeys = 1;
+module_param_named(pb_enablefnkeys, enablefnkeys, bool, 0644);
+MODULE_PARM_DESC(enablefnkeys, "Enable fn special keys");
+
+static int enablekeypad = 1;
+module_param_named(pb_enablekeypad, enablekeypad, bool, 0644);
+MODULE_PARM_DESC(enablekeypad, "Enable keypad keys");
+
+static inline struct input_pb_translation *find_translation(
+	struct input_pb_translation *table, int from)
+{
+	struct input_pb_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && !(trans->from == from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int input_pb_init(struct hid_device* hid)
+{
+	hid->hook_private = kzalloc(sizeof(struct input_pb_data), GFP_KERNEL);
+
+	if (!hid->hook_private)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void input_pb_exit(struct hid_device* hid)
+{
+	kfree(hid->hook_private);
+
+	return;
+}
+
+static int input_pb_event(struct hid_device *hid, struct hid_field *field,
+			  struct hid_usage *usage, __s32 value, struct pt_regs *regs)
+{
+	struct input_pb_data *private = hid->hook_private;
+	struct input_dev *input;
+	struct input_pb_translation *trans;
+
+	if (usage->type != EV_KEY)
+		return -1;
+
+	input = field->hidinput->input;
+
+	switch(usage->code) {
+	case KEY_FN:
+		if (value) private->enabled_keys |= PB_ENABLED_FN;
+		else	   private->enabled_keys &= ~PB_ENABLED_FN;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 0;
+	}
+
+	if (enablefnkeys) {
+		trans = find_translation(fn_keys, usage->code);
+
+		if (trans) {
+			int known_key;
+
+			if (trans->flags & PB_FLAG_FKEY)
+				known_key =
+					( fkeysfirst &&  (private->enabled_keys & PB_ENABLED_FN)) ||
+					(!fkeysfirst && !(private->enabled_keys & PB_ENABLED_FN));
+			else
+				known_key = (private->enabled_keys & PB_ENABLED_FN);
+
+			if (known_key) {
+				input_event(input, usage->type, trans->to, value);
+				return 0;
+			}
+		}
+	}
+
+	if (enablekeypad && test_bit(LED_NUML, input->led)) {
+		trans = find_translation(numlock_keys, usage->code);
+		if (trans)
+			input_event(input, usage->type, trans->to, value);
+
+		return 0;
+	}
+
+	return -1;
+}
+
+static int input_pb_conf(struct hid_input *hidinput, struct hid_field *field,
+			 struct hid_usage *usage)
+{
+	struct input_dev *input = hidinput->input;
+	struct input_pb_translation *trans;
+	int max = 0;
+	unsigned long *bit = NULL;
+
+	field->hidinput = hidinput;
+
+	switch (usage->hid & HID_USAGE_PAGE) {
+	case HID_UP_CUSTOM:
+		switch(usage->hid & HID_USAGE) {
+		/* The fn key on Apple PowerBooks */
+		case 0x003:
+			map_key_clear(KEY_FN);
+
+			set_bit(KEY_FN, input->keybit);
+			set_bit(KEY_NUMLOCK, input->keybit);
+
+			/* Enable all needed keys */
+			for(trans = fn_keys; trans->from; trans++)
+				set_bit(trans->to, input->keybit);
+
+			for(trans = numlock_keys; trans->from; trans++)
+				set_bit(trans->to, input->keybit);
+
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+struct hid_input_hook usbhid_input_powerbook = {
+	.devices = input_pb_devices,
+	.init = input_pb_init,
+	.conf = input_pb_conf,
+	.event = input_pb_event,
+	.exit = input_pb_exit,
+};
diff -rNup linux-2.6.15-rc6.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc6/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc6.orig/drivers/usb/input/hid-input.c	2005-12-22 20:51:57.000000000 +0100
+++ linux-2.6.15-rc6/drivers/usb/input/hid-input.c	2005-12-25 21:48:09.000000000 +0100
@@ -63,6 +63,16 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+/*
+ * This table contains pointers to hook structures.
+ */
+static struct hid_input_hook* hooks[] = {
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	&usbhid_input_powerbook,
+#endif
+	NULL /* Terminating entry */
+};
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +83,33 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static int hidinput_hook_wants_device(struct hid_input_hook *hook, u16 idVendor, u16 idProduct)
+{
+	struct hid_input_hook_device *curdev;
+
+	if (!hook->devices)
+		return 0;
+
+	/* Try to find device */
+	for (curdev = hook->devices;                                 
+	     curdev->idVendor && !(curdev->idVendor == idVendor && curdev->idProduct == idProduct);
+	     curdev++);
+
+	return !!curdev->idVendor;
+}
+
+static struct hid_input_hook *hid_input_get_hook(u16 idVendor, u16 idProduct)
+{
+	struct hid_input_hook **hook;
+
+	for (hook = hooks;
+	     *hook && !hidinput_hook_wants_device(*hook, idVendor, idProduct);
+	     hook++);
+
+	return *hook;
+}
+
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -92,6 +129,12 @@ static void hidinput_configure_usage(str
 	if (field->flags & HID_MAIN_ITEM_CONSTANT)
 		goto ignore;
 
+	if(field->hidinput->hook) {
+		struct hid_input_hook *hook = field->hidinput->hook;
+		if(hook->conf && hook->conf(hidinput, field, usage) == 0)
+			return;
+	}
+
 	switch (usage->hid & HID_USAGE_PAGE) {
 
 		case HID_UP_UNDEFINED:
@@ -325,7 +368,6 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
 				default:    goto ignore;
 			}
 			break;
@@ -470,6 +512,12 @@ void hidinput_hid_event(struct hid_devic
 	if (!usage->type)
 		return;
 
+	if(field->hidinput->hook) {
+		struct hid_input_hook *hook = field->hidinput->hook;
+		if(hook->event && hook->event(hid, field, usage, value, regs) == 0)
+			return;
+	}
+
 	if (((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_5) && (usage->hid == 0x00090005))
 		|| ((hid->quirks & HID_QUIRK_2WHEEL_MOUSE_HACK_7) && (usage->hid == 0x00090007))) {
 		if (value) hid->quirks |=  HID_QUIRK_2WHEEL_MOUSE_HACK_ON;
@@ -648,6 +696,17 @@ int hidinput_connect(struct hid_device *
 				list_add_tail(&hidinput->list, &hid->inputs);
 			}
 
+			hidinput->hook = hid_input_get_hook(
+				le16_to_cpu(hid->dev->descriptor.idVendor),
+				le16_to_cpu(hid->dev->descriptor.idProduct));
+
+			if(hidinput->hook &&
+			   hidinput->hook->init &&
+			   hidinput->hook->init(hid) < 0) {
+				printk(KERN_ERR "input: Hook initialization failed.\n");
+				hidinput->hook = NULL;
+			}
+
 			for (i = 0; i < report->maxfield; i++)
 				for (j = 0; j < report->field[i]->maxusage; j++)
 					hidinput_configure_usage(hidinput, report->field[i],
@@ -681,6 +740,8 @@ void hidinput_disconnect(struct hid_devi
 	struct hid_input *hidinput, *next;
 
 	list_for_each_entry_safe(hidinput, next, &hid->inputs, list) {
+		if(hidinput->hook && hidinput->hook->exit)
+			hidinput->hook->exit(hid);
 		list_del(&hidinput->list);
 		input_unregister_device(hidinput->input);
 		kfree(hidinput);
diff -rNup linux-2.6.15-rc6.orig/drivers/usb/input/hid.h linux-2.6.15-rc6/drivers/usb/input/hid.h
--- linux-2.6.15-rc6.orig/drivers/usb/input/hid.h	2005-12-22 20:51:57.000000000 +0100
+++ linux-2.6.15-rc6/drivers/usb/input/hid.h	2005-12-25 21:50:33.000000000 +0100
@@ -368,10 +368,33 @@ struct hid_control_fifo {
 #define HID_CTRL_RUNNING	1
 #define HID_OUT_RUNNING		2
 
+struct hid_input_hook_device {
+	u16 idVendor;
+	u16 idProduct;
+};
+
+struct hid_input_hook {
+	struct list_head list;
+	struct hid_input_hook_device *devices;
+
+	int (*init)(struct hid_device*);
+	void (*exit)(struct hid_device*);
+	int (*event)(struct hid_device *hid, struct hid_field *field,
+		     struct hid_usage *usage, __s32 value, struct pt_regs *regs);
+	int (*conf)(struct hid_input *hidinput, struct hid_field *field,
+		    struct hid_usage *usage);
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+extern struct hid_input_hook usbhid_input_powerbook;
+#endif
+
 struct hid_input {
 	struct list_head list;
 	struct hid_report *report;
 	struct input_dev *input;
+
+	struct hid_input_hook *hook;
 };
 
 struct hid_device {							/* device report descriptor */
@@ -431,6 +454,8 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+	void *hook_private;
 };
 
 #define HID_GLOBAL_STACK_SIZE 4

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-25 21:20 [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks Michael Hanselmann
@ 2005-12-25 21:57 ` Benjamin Herrenschmidt
  2005-12-26  4:04 ` Dmitry Torokhov
  1 sibling, 0 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2005-12-25 21:57 UTC (permalink / raw)
  To: Michael Hanselmann; +Cc: linux-kernel, linux-input, linuxppc-dev, linux-kernel

On Sun, 2005-12-25 at 22:20 +0100, Michael Hanselmann wrote:
> This patch adds a basic input hook support to usbhid because the quirks
> method is outrunning the available bits. A hook for the Fn and Numlock
> keys on Apple PowerBooks is included.
> 
> Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
> Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
> Acked-by: Johannes Berg <johannes@sipsolutions.net>

Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

This makes the keyboard useable on latest generations of Apple
PowerBooks, so please apply asap :)

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-25 21:20 [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks Michael Hanselmann
  2005-12-25 21:57 ` Benjamin Herrenschmidt
@ 2005-12-26  4:04 ` Dmitry Torokhov
  2005-12-26  5:46   ` Benjamin Herrenschmidt
  2005-12-31 23:51   ` Michael Hanselmann
  1 sibling, 2 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2005-12-26  4:04 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: linux-kernel, linux-input, linuxppc-dev, benh, linux-kernel,
	Vojtech Pavlik

On Sunday 25 December 2005 16:20, Michael Hanselmann wrote:
> This patch adds a basic input hook support to usbhid because the quirks
> method is outrunning the available bits. A hook for the Fn and Numlock
> keys on Apple PowerBooks is included.

Well, we have used 11 out of 32 available bits so there still some
reserves. My concern is that your implementation allows only one
hook to be installed while with quirks you can have several of them
active per device.

As far as the hook itself - i have that feeling that it should not be
done in kernel but via a keymap.
 
-- 
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-26  4:04 ` Dmitry Torokhov
@ 2005-12-26  5:46   ` Benjamin Herrenschmidt
  2006-01-11 21:07     ` Dmitry Torokhov
  2005-12-31 23:51   ` Michael Hanselmann
  1 sibling, 1 reply; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2005-12-26  5:46 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Sun, 2005-12-25 at 23:04 -0500, Dmitry Torokhov wrote:
> On Sunday 25 December 2005 16:20, Michael Hanselmann wrote:
> > This patch adds a basic input hook support to usbhid because the quirks
> > method is outrunning the available bits. A hook for the Fn and Numlock
> > keys on Apple PowerBooks is included.
> 
> Well, we have used 11 out of 32 available bits so there still some
> reserves. My concern is that your implementation allows only one
> hook to be installed while with quirks you can have several of them
> active per device.

I haven't looked at the details so I can't comment much there, though
the hook has the interest of making it possible to have it in a module
that gets separately loaded if necessary, no ? Maybe the driver could
maintain a list of hooks ?

> As far as the hook itself - i have that feeling that it should not be
> done in kernel but via a keymap.

While I understand your feeling, it's a bit annoying in this specific
case because previous models did this in hardware and all mac keymaps
already account for that. Knowing how nasty it has been to get mac
keymaps updated and in a good shape, and to get distros to properly get
them, it makes a lot of sense to have this small hook in the kernel that
makes the USB keyboard behave exactly like the older ADB couterparts.

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-26  4:04 ` Dmitry Torokhov
  2005-12-26  5:46   ` Benjamin Herrenschmidt
@ 2005-12-31 23:51   ` Michael Hanselmann
  2006-01-01  1:33     ` Michael Hanselmann
                       ` (2 more replies)
  1 sibling, 3 replies; 46+ messages in thread
From: Michael Hanselmann @ 2005-12-31 23:51 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-input, linuxppc-dev, benh, linux-kernel,
	Vojtech Pavlik

On Sun, Dec 25, 2005 at 11:04:30PM -0500, Dmitry Torokhov wrote:
> Well, we have used 11 out of 32 available bits so there still some
> reserves. My concern is that your implementation allows only one
> hook to be installed while with quirks you can have several of them
> active per device.

Below you find an implementation using quirks:

---
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2005-12-31 22:39:53.000000000 +0100
@@ -1580,6 +1580,10 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc7/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-input.c	2005-12-31 23:49:42.000000000 +0100
@@ -63,6 +63,66 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct input_pb_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct input_pb_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct input_pb_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_ENTER,	KEY_KPENTER },
+	{ }
+};
+
+static int powerbook_fkeysfirst = 1;
+module_param_named(pb_fkeysfirst, powerbook_fkeysfirst, bool, 0644);
+MODULE_PARM_DESC(powerbook_fkeysfirst, "Use fn special keys only while pressing fn");
+
+static int powerbook_enablefnkeys = 1;
+module_param_named(pb_enablefnkeys, powerbook_enablefnkeys, bool, 0644);
+MODULE_PARM_DESC(powerbook_enablefnkeys, "Enable fn special keys");
+
+static int powerbook_enablekeypad = 1;
+module_param_named(pb_enablekeypad, powerbook_enablekeypad, bool, 0644);
+MODULE_PARM_DESC(powerbook_enablekeypad, "Enable keypad keys");
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +133,17 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct input_pb_translation *find_translation(
+	struct input_pb_translation *table, int from)
+{
+	struct input_pb_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && !(trans->from == from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -94,7 +165,7 @@ static void hidinput_configure_usage(str
 
 	switch (usage->hid & HID_USAGE_PAGE) {
 
-		case HID_UP_UNDEFINED:
+	case HID_UP_UNDEFINED:
 			goto ignore;
 
 		case HID_UP_KEYBOARD:
@@ -325,7 +396,25 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct input_pb_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +571,49 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if (hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) {
+		struct input_pb_translation *trans;
+
+		switch(usage->code) {
+		case KEY_FN:
+			if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+			else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+			input_event(input, usage->type, usage->code, value);
+
+			return;
+		}
+
+		if (powerbook_enablefnkeys) {
+			trans = find_translation(powerbook_fn_keys, usage->code);
+
+			if (trans) {
+				int known_key;
+
+				if (trans->flags & POWERBOOK_FLAG_FKEY)
+					known_key =
+						( powerbook_fkeysfirst &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+						(!powerbook_fkeysfirst && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+				else
+					known_key = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+				if (known_key) {
+					input_event(input, usage->type, trans->to, value);
+					return;
+				}
+			}
+		}
+
+		if (powerbook_enablekeypad && test_bit(LED_NUML, input->led)) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans)
+				input_event(input, usage->type, trans->to, value);
+
+			return;
+		}
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid.h linux-2.6.15-rc7/drivers/usb/input/hid.h
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid.h	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid.h	2005-12-31 22:39:49.000000000 +0100
@@ -246,6 +246,9 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x0000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x0001000
+#define HID_QUIRK_POWERBOOK_NUMLOCK_ON		0x0002000
 
 /*
  * This is the global environment of the parser. This information is

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-31 23:51   ` Michael Hanselmann
@ 2006-01-01  1:33     ` Michael Hanselmann
  2006-01-01  2:56     ` Benjamin Herrenschmidt
  2006-01-02 12:06     ` Stelian Pop
  2 siblings, 0 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-01  1:33 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-kernel, linux-input, linuxppc-dev, benh, linux-kernel,
	Vojtech Pavlik

On Sun, Jan 01, 2006 at 12:51:24AM +0100, Michael Hanselmann wrote:
> +#define HID_QUIRK_POWERBOOK_NUMLOCK_ON		0x0002000

Aww, please ignore that one when looking at the patch. It's not used and
I'll resubmit the patch anyway with a full Signed-off-by & Co. once you
approve it.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-31 23:51   ` Michael Hanselmann
  2006-01-01  1:33     ` Michael Hanselmann
@ 2006-01-01  2:56     ` Benjamin Herrenschmidt
  2006-01-01  3:03       ` Michael Hanselmann
  2006-01-02 22:46       ` Michael Hanselmann
  2006-01-02 12:06     ` Stelian Pop
  2 siblings, 2 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-01  2:56 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Dmitry Torokhov, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Sun, 2006-01-01 at 00:51 +0100, Michael Hanselmann wrote:
> On Sun, Dec 25, 2005 at 11:04:30PM -0500, Dmitry Torokhov wrote:
> > Well, we have used 11 out of 32 available bits so there still some
> > reserves. My concern is that your implementation allows only one
> > hook to be installed while with quirks you can have several of them
> > active per device.
> 
> Below you find an implementation using quirks:

I've been using the other patch for some time now and while it's a
life-saver, it does have one annoying little issue: If you press a key
with the Fn key down and release that key with the Fn key up, your key
is stuck. That is, the patch changes the keycode for Up & Down events
separately based on the Fn state at the time of the event.

What should be done is that when you release a key, you send the key up
with the keycode that matches the Fn state at the time the key was
pressed. That can easily be done using a simple bitmap that keeps track
of the Fn state on keydown for the various keycodes.

Ben.
  


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-01  2:56     ` Benjamin Herrenschmidt
@ 2006-01-01  3:03       ` Michael Hanselmann
  2006-01-01  6:09         ` Benjamin Herrenschmidt
  2006-01-02 22:46       ` Michael Hanselmann
  1 sibling, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-01  3:03 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Dmitry Torokhov, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Sun, Jan 01, 2006 at 01:56:47PM +1100, Benjamin Herrenschmidt wrote:
> I've been using the other patch for some time now and while it's a
> life-saver, it does have one annoying little issue: If you press a key
> with the Fn key down and release that key with the Fn key up, your key
> is stuck.

I noticed something like that with the numlock key as well but didn't
have time to fix it yet.

Other than that, have you found any keys that aren't mapped or are
mapped incorrectly?

The new patch adds quite some code to hid-input.c, should it be
configuratible via .config?

Thanks,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-01  3:03       ` Michael Hanselmann
@ 2006-01-01  6:09         ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-01  6:09 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Dmitry Torokhov, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Sun, 2006-01-01 at 04:03 +0100, Michael Hanselmann wrote:
> On Sun, Jan 01, 2006 at 01:56:47PM +1100, Benjamin Herrenschmidt wrote:
> > I've been using the other patch for some time now and while it's a
> > life-saver, it does have one annoying little issue: If you press a key
> > with the Fn key down and release that key with the Fn key up, your key
> > is stuck.
> 
> I noticed something like that with the numlock key as well but didn't
> have time to fix it yet.
> 
> Other than that, have you found any keys that aren't mapped or are
> mapped incorrectly?

I haven't but then I haven't tested in depth neither. Such little bugs
if any can always be fixed later anyway.

> The new patch adds quite some code to hid-input.c, should it be
> configuratible via .config?

Possibly yes, since it's only useful for mac laptops...

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-31 23:51   ` Michael Hanselmann
  2006-01-01  1:33     ` Michael Hanselmann
  2006-01-01  2:56     ` Benjamin Herrenschmidt
@ 2006-01-02 12:06     ` Stelian Pop
  2 siblings, 0 replies; 46+ messages in thread
From: Stelian Pop @ 2006-01-02 12:06 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Dmitry Torokhov, linux-kernel, linux-kernel, linuxppc-dev,
	Vojtech Pavlik, linux-input

Le dimanche 01 janvier 2006 à 00:51 +0100, Michael Hanselmann a écrit :
> On Sun, Dec 25, 2005 at 11:04:30PM -0500, Dmitry Torokhov wrote:
> > Well, we have used 11 out of 32 available bits so there still some
> > reserves. My concern is that your implementation allows only one
> > hook to be installed while with quirks you can have several of them
> > active per device.
> 
> Below you find an implementation using quirks:
> 
> ---
> diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
> --- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
> +++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2005-12-31 22:39:53.000000000 +0100
> @@ -1580,6 +1580,10 @@ static struct hid_blacklist {
>  	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
>  	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
>  
> +	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
> +

Works fine for me on a slightly older (jul '05) Powerbook after adding
the following USB identifiers:

+       { USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+       { USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+       { USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+       { USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },

Mine is a 020F but the other three ones should be fine too (those are
taken from the appletouch trackpad driver).

Stelian.
-- 
Stelian Pop <stelian@popies.net>


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-01  2:56     ` Benjamin Herrenschmidt
  2006-01-01  3:03       ` Michael Hanselmann
@ 2006-01-02 22:46       ` Michael Hanselmann
  2006-01-03  2:29         ` Ben Collins
  1 sibling, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-02 22:46 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Dmitry Torokhov, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik, stelian

On Sun, Jan 01, 2006 at 01:56:47PM +1100, Benjamin Herrenschmidt wrote:
> I've been using the other patch for some time now and while it's a
> life-saver, it does have one annoying little issue: If you press a key
> with the Fn key down and release that key with the Fn key up, your key
> is stuck.

Here's another patch with this and some other issues fixed.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig linux-2.6.15-rc7/drivers/usb/input/Kconfig
--- linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/Kconfig	2006-01-02 20:26:21.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2006-01-02 13:52:59.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc7/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-input.c	2006-01-02 23:35:36.000000000 +0100
@@ -63,6 +63,70 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_ENTER,	KEY_KPENTER },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ }
+};
+
+static int powerbook_fkeysfirst = 1;
+module_param_named(pb_fkeysfirst, powerbook_fkeysfirst, bool, 0644);
+MODULE_PARM_DESC(powerbook_fkeysfirst, "Use fn special keys only while pressing fn");
+
+static int powerbook_enablefnkeys = 1;
+module_param_named(pb_enablefnkeys, powerbook_enablefnkeys, bool, 0644);
+MODULE_PARM_DESC(powerbook_enablefnkeys, "Enable fn special keys");
+
+static int powerbook_enablekeypad = 1;
+module_param_named(pb_enablekeypad, powerbook_enablekeypad, bool, 0644);
+MODULE_PARM_DESC(powerbook_enablekeypad, "Enable keypad keys");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +137,85 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, int from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && !(trans->from == from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (powerbook_enablefnkeys) {
+		trans = find_translation(powerbook_fn_keys, usage->code);
+
+		if (trans) {
+			int do_translate;
+
+			if (test_bit(usage->code, hid->pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					( powerbook_fkeysfirst &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(!powerbook_fkeysfirst && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_fn);
+				else
+					clear_bit(usage->code, hid->pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+	}
+
+	if (powerbook_enablekeypad) {
+		int try_translate =
+			test_bit(usage->code, hid->pb_numlock)?1:
+			test_bit(LED_NUML, input->led);
+
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_numlock);
+				else
+					clear_bit(usage->code, hid->pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +468,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +645,11 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value)) {
+		return;
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid.h linux-2.6.15-rc7/drivers/usb/input/hid.h
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid.h	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid.h	2006-01-02 21:44:15.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x0000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x0001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,14 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	/* We do this here because it's only relevant for the
+	 * USB devices, not for all input_dev's.
+	 */
+	unsigned long pb_fn[NBITS(KEY_MAX)];
+	unsigned long pb_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-02 22:46       ` Michael Hanselmann
@ 2006-01-03  2:29         ` Ben Collins
  2006-01-03 19:14           ` Michael Hanselmann
  0 siblings, 1 reply; 46+ messages in thread
From: Ben Collins @ 2006-01-03  2:29 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Benjamin Herrenschmidt, Dmitry Torokhov, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel, Vojtech Pavlik, stelian

On Mon, 2006-01-02 at 23:46 +0100, Michael Hanselmann wrote:

> +static int powerbook_fkeysfirst = 1;
> +module_param_named(pb_fkeysfirst, powerbook_fkeysfirst, bool, 0644);
> +MODULE_PARM_DESC(powerbook_fkeysfirst, "Use fn special keys only while pressing fn");
> +
> +static int powerbook_enablefnkeys = 1;
> +module_param_named(pb_enablefnkeys, powerbook_enablefnkeys, bool, 0644);
> +MODULE_PARM_DESC(powerbook_enablefnkeys, "Enable fn special keys");
> +
> +static int powerbook_enablekeypad = 1;
> +module_param_named(pb_enablekeypad, powerbook_enablekeypad, bool, 0644);
> +MODULE_PARM_DESC(powerbook_enablekeypad, "Enable keypad keys");
> +#endif

I think these should be inverted to, something like
pbook_disable_keypad, pbook_disable_fnkeys and pbook_fnfirst.

Two reasons. First, it just makes more sense to pass a module param to
turn something on (doing powerbook_enablekeypad=0 isn't as intuitive as
pbook_disable_keypad=1). Second reason is that since these are static
vars, defaulting them to uninitialized (leaving them in the bss, as 0)
reduces binary size.

-- 
   Ben Collins <ben.collins@ubuntu.com>
   Developer
   Ubuntu Linux


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-03  2:29         ` Ben Collins
@ 2006-01-03 19:14           ` Michael Hanselmann
  2006-01-03 19:18             ` Ben Collins
  0 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-03 19:14 UTC (permalink / raw)
  To: Ben Collins
  Cc: Benjamin Herrenschmidt, Dmitry Torokhov, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel, Vojtech Pavlik, stelian

Hello Ben

On Mon, Jan 02, 2006 at 09:29:13PM -0500, Ben Collins wrote:
> I think these should be inverted to, something like
> pbook_disable_keypad, pbook_disable_fnkeys and pbook_fnfirst.

I've changed that in the patch below:

---
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig linux-2.6.15-rc7/drivers/usb/input/Kconfig
--- linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/Kconfig	2006-01-02 20:26:21.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2006-01-02 13:52:59.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc7/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-input.c	2006-01-03 19:57:26.000000000 +0100
@@ -63,6 +63,71 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fkeyslast = 0;
+module_param_named(pb_fkeyslast, usbhid_pb_fkeyslast, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_fkeyslast, "Use F keys only while pressing fn on PowerBooks");
+
+static int usbhid_pb_disablefnkeys = 0;
+module_param_named(pb_disablefnkeys, usbhid_pb_disablefnkeys, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablefnkeys, "Disable fn special keys on PowerBooks");
+
+static int usbhid_pb_disablekeypad = 0;
+module_param_named(pb_disablekeypad, usbhid_pb_disablekeypad, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablekeypad, "Disable keypad keys on PowerBooks");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +138,85 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (!usbhid_pb_disablefnkeys) {
+		trans = find_translation(powerbook_fn_keys, usage->code);
+
+		if (trans) {
+			int do_translate;
+
+			if (test_bit(usage->code, hid->pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(!usbhid_pb_fkeyslast &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					( usbhid_pb_fkeyslast && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_fn);
+				else
+					clear_bit(usage->code, hid->pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+	}
+
+	if (!usbhid_pb_disablekeypad) {
+		int try_translate =
+			test_bit(usage->code, hid->pb_numlock)?1:
+			test_bit(LED_NUML, input->led);
+
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_numlock);
+				else
+					clear_bit(usage->code, hid->pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +469,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +646,11 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value)) {
+		return;
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid.h linux-2.6.15-rc7/drivers/usb/input/hid.h
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid.h	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid.h	2006-01-03 00:45:13.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,14 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	/* We do this here because it's only relevant for the
+	 * USB devices, not for all input_dev's.
+	 */
+	unsigned long pb_fn[NBITS(KEY_MAX)];
+	unsigned long pb_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-03 19:14           ` Michael Hanselmann
@ 2006-01-03 19:18             ` Ben Collins
  2006-01-03 19:25               ` Michael Hanselmann
  0 siblings, 1 reply; 46+ messages in thread
From: Ben Collins @ 2006-01-03 19:18 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Benjamin Herrenschmidt, Dmitry Torokhov, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel, Vojtech Pavlik, stelian


> +static int usbhid_pb_fkeyslast = 0;
> +module_param_named(pb_fkeyslast, usbhid_pb_fkeyslast, bool, 0644);
> +MODULE_PARM_DESC(usbhid_pb_fkeyslast, "Use F keys only while pressing fn on PowerBooks");
> +
> +static int usbhid_pb_disablefnkeys = 0;
> +module_param_named(pb_disablefnkeys, usbhid_pb_disablefnkeys, bool, 0644);
> +MODULE_PARM_DESC(usbhid_pb_disablefnkeys, "Disable fn special keys on PowerBooks");
> +
> +static int usbhid_pb_disablekeypad = 0;
> +module_param_named(pb_disablekeypad, usbhid_pb_disablekeypad, bool, 0644);
> +MODULE_PARM_DESC(usbhid_pb_disablekeypad, "Disable keypad keys on PowerBooks");
> +#endif

For it to be useful, you have to not initialize them (since they are
static, they will be zero'd anyway). Initializing them defeats the
purpose.

static int usbhid_pb_fkeyslast;
etc.

-- 
   Ben Collins <ben.collins@ubuntu.com>
   Developer
   Ubuntu Linux


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-03 19:18             ` Ben Collins
@ 2006-01-03 19:25               ` Michael Hanselmann
  0 siblings, 0 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-03 19:25 UTC (permalink / raw)
  To: Ben Collins
  Cc: Benjamin Herrenschmidt, Dmitry Torokhov, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel, Vojtech Pavlik, stelian

On Tue, Jan 03, 2006 at 02:18:49PM -0500, Ben Collins wrote:
> For it to be useful, you have to not initialize them (since they are
> static, they will be zero'd anyway). Initializing them defeats the
> purpose.

Dang, should have known that. I feel somewhat guilty having to send so
many patches. Anyway, here's another one with that small issue fixed:

---
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig linux-2.6.15-rc7/drivers/usb/input/Kconfig
--- linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/Kconfig	2006-01-02 20:26:21.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2006-01-02 13:52:59.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc7/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-input.c	2006-01-03 20:21:00.000000000 +0100
@@ -63,6 +63,71 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fkeyslast;
+module_param_named(pb_fkeyslast, usbhid_pb_fkeyslast, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_fkeyslast, "Use F keys only while pressing fn on PowerBooks");
+
+static int usbhid_pb_disablefnkeys;
+module_param_named(pb_disablefnkeys, usbhid_pb_disablefnkeys, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablefnkeys, "Disable fn special keys on PowerBooks");
+
+static int usbhid_pb_disablekeypad;
+module_param_named(pb_disablekeypad, usbhid_pb_disablekeypad, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablekeypad, "Disable keypad keys on PowerBooks");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +138,85 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (!usbhid_pb_disablefnkeys) {
+		trans = find_translation(powerbook_fn_keys, usage->code);
+
+		if (trans) {
+			int do_translate;
+
+			if (test_bit(usage->code, hid->pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(!usbhid_pb_fkeyslast &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					( usbhid_pb_fkeyslast && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_fn);
+				else
+					clear_bit(usage->code, hid->pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+	}
+
+	if (!usbhid_pb_disablekeypad) {
+		int try_translate =
+			test_bit(usage->code, hid->pb_numlock)?1:
+			test_bit(LED_NUML, input->led);
+
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_numlock);
+				else
+					clear_bit(usage->code, hid->pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +469,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +646,11 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value)) {
+		return;
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid.h linux-2.6.15-rc7/drivers/usb/input/hid.h
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid.h	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid.h	2006-01-03 00:45:13.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,14 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	/* We do this here because it's only relevant for the
+	 * USB devices, not for all input_dev's.
+	 */
+	unsigned long pb_fn[NBITS(KEY_MAX)];
+	unsigned long pb_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2005-12-26  5:46   ` Benjamin Herrenschmidt
@ 2006-01-11 21:07     ` Dmitry Torokhov
  2006-01-11 21:20       ` Michael Hanselmann
                         ` (2 more replies)
  0 siblings, 3 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-11 21:07 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On 12/26/05, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> On Sun, 2005-12-25 at 23:04 -0500, Dmitry Torokhov wrote:
>
> > As far as the hook itself - i have that feeling that it should not be
> > done in kernel but via a keymap.
>
> While I understand your feeling, it's a bit annoying in this specific
> case because previous models did this in hardware and all mac keymaps
> already account for that. Knowing how nasty it has been to get mac
> keymaps updated and in a good shape, and to get distros to properly get
> them, it makes a lot of sense to have this small hook in the kernel that
> makes the USB keyboard behave exactly like the older ADB couterparts.
>

Ok, I am looking at the patch again, and I have a question - do we
really need these 3 module parameters? If the goal is to be compatible
with older keyboards then shouldn't we stick to one behavior?

--
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:07     ` Dmitry Torokhov
@ 2006-01-11 21:20       ` Michael Hanselmann
  2006-01-11 21:34         ` Benjamin Herrenschmidt
  2006-01-11 21:30       ` Benjamin Herrenschmidt
  2006-01-11 23:26       ` Michael Hanselmann
  2 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 21:20 UTC (permalink / raw)
  To: dtor_core
  Cc: Benjamin Herrenschmidt, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Wed, Jan 11, 2006 at 04:07:37PM -0500, Dmitry Torokhov wrote:
> Ok, I am looking at the patch again, and I have a question - do we
> really need these 3 module parameters? If the goal is to be compatible
> with older keyboards then shouldn't we stick to one behavior?

The old keyboard was controlled by ADB (Apple Desktop Bus) commands sent
by pbbuttonsd. That doesn't work for the USB keyboard because the
conversion is not done in hardware like with the old ones. ioctl's would
also be possible, but I'm not sure wether they would be easy to do for
USB devices. Module parameters can be changed using sysfs.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:07     ` Dmitry Torokhov
  2006-01-11 21:20       ` Michael Hanselmann
@ 2006-01-11 21:30       ` Benjamin Herrenschmidt
  2006-01-11 21:45         ` Vojtech Pavlik
  2006-01-11 21:46         ` Michael Hanselmann
  2006-01-11 23:26       ` Michael Hanselmann
  2 siblings, 2 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-11 21:30 UTC (permalink / raw)
  To: dtor_core
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik


> Ok, I am looking at the patch again, and I have a question - do we
> really need these 3 module parameters? If the goal is to be compatible
> with older keyboards then shouldn't we stick to one behavior?

I personally think one parameter is plenty enough (on/off) . Michael,
can you send Dimitri the latest version of that patch please ?

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:20       ` Michael Hanselmann
@ 2006-01-11 21:34         ` Benjamin Herrenschmidt
  2006-01-11 21:38           ` Michael Hanselmann
  0 siblings, 1 reply; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-11 21:34 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Wed, 2006-01-11 at 22:20 +0100, Michael Hanselmann wrote:
> On Wed, Jan 11, 2006 at 04:07:37PM -0500, Dmitry Torokhov wrote:
> > Ok, I am looking at the patch again, and I have a question - do we
> > really need these 3 module parameters? If the goal is to be compatible
> > with older keyboards then shouldn't we stick to one behavior?
> 
> The old keyboard was controlled by ADB (Apple Desktop Bus) commands sent
> by pbbuttonsd. That doesn't work for the USB keyboard because the
> conversion is not done in hardware like with the old ones. ioctl's would
> also be possible, but I'm not sure wether they would be easy to do for
> USB devices. Module parameters can be changed using sysfs.

Yeah, but the question is why 3 ? I think one (on/off) is enough. Do you
have any case where people actually change the other ones ?

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:34         ` Benjamin Herrenschmidt
@ 2006-01-11 21:38           ` Michael Hanselmann
  2006-01-11 21:41             ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 21:38 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Thu, Jan 12, 2006 at 08:34:17AM +1100, Benjamin Herrenschmidt wrote:
> Yeah, but the question is why 3 ? I think one (on/off) is enough. Do you
> have any case where people actually change the other ones ?

Johannes Berg told me he wants to use the fn key alone to switch the
keyboard layout or something. For such uses, the pb_enablefn is there.
pb_fkeyslast is to emulate the behaviour of KBDMode from pbbuttonsd.
The last one, pb_disablekeypad could left out. It doesn't add much code
and might be used by some people, too.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:38           ` Michael Hanselmann
@ 2006-01-11 21:41             ` Benjamin Herrenschmidt
  2006-01-11 21:43               ` Michael Hanselmann
  0 siblings, 1 reply; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-11 21:41 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Wed, 2006-01-11 at 22:38 +0100, Michael Hanselmann wrote:
> On Thu, Jan 12, 2006 at 08:34:17AM +1100, Benjamin Herrenschmidt wrote:
> > Yeah, but the question is why 3 ? I think one (on/off) is enough. Do you
> > have any case where people actually change the other ones ?
> 
> Johannes Berg told me he wants to use the fn key alone to switch the
> keyboard layout or something. For such uses, the pb_enablefn is there.

What does it do ? Just send a keycode ? That should be unconditionnal.
The Fn key should change a keycode always. I don't see why you would
that to be off.

> pb_fkeyslast is to emulate the behaviour of KBDMode from pbbuttonsd.
> The last one, pb_disablekeypad could left out. It doesn't add much code
> and might be used by some people, too.

The ony one we need is the one enabling/disabing the old behaviour.

Ben.




^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:41             ` Benjamin Herrenschmidt
@ 2006-01-11 21:43               ` Michael Hanselmann
  2006-01-11 21:47                 ` Vojtech Pavlik
  2006-01-11 21:54                 ` Benjamin Herrenschmidt
  0 siblings, 2 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 21:43 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Thu, Jan 12, 2006 at 08:41:08AM +1100, Benjamin Herrenschmidt wrote:
> > Johannes Berg told me he wants to use the fn key alone to switch the
> > keyboard layout or something. For such uses, the pb_enablefn is there.

Sorry, actually it's called pb_disablefn now.

> What does it do ? Just send a keycode ? That should be unconditionnal.
> The Fn key should change a keycode always. I don't see why you would
> that to be off.

No, if that parameter is disabled, it translates key combinations like
Fn+F1 to KEY_BRIGHTNESSUP. If it's enabled, it only sends KEY_FN.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:30       ` Benjamin Herrenschmidt
@ 2006-01-11 21:45         ` Vojtech Pavlik
  2006-01-11 21:46         ` Michael Hanselmann
  1 sibling, 0 replies; 46+ messages in thread
From: Vojtech Pavlik @ 2006-01-11 21:45 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dtor_core, Michael Hanselmann, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Thu, Jan 12, 2006 at 08:30:06AM +1100, Benjamin Herrenschmidt wrote:

> > Ok, I am looking at the patch again, and I have a question - do we
> > really need these 3 module parameters? If the goal is to be compatible
> > with older keyboards then shouldn't we stick to one behavior?
> 
> I personally think one parameter is plenty enough (on/off) . Michael,
> can you send Dimitri the latest version of that patch please ?
 
I agree. If you need the compatible behavior.

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:30       ` Benjamin Herrenschmidt
  2006-01-11 21:45         ` Vojtech Pavlik
@ 2006-01-11 21:46         ` Michael Hanselmann
  1 sibling, 0 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 21:46 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Thu, Jan 12, 2006 at 08:30:06AM +1100, Benjamin Herrenschmidt wrote:
> I personally think one parameter is plenty enough (on/off) . Michael,
> can you send Dimitri the latest version of that patch please ?

Here it is, CC to Dmitry.

This patch implements support for the fn key on PowerBooks using USB
based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>                      
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>                  
Acked-by: Johannes Berg <johannes@sipsolutions.net>                             
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig linux-2.6.15-rc7/drivers/usb/input/Kconfig
--- linux-2.6.15-rc7.orig/drivers/usb/input/Kconfig	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/Kconfig	2006-01-02 20:26:21.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c linux-2.6.15-rc7/drivers/usb/input/hid-core.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-core.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-core.c	2006-01-02 13:52:59.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c linux-2.6.15-rc7/drivers/usb/input/hid-input.c
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid-input.c	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid-input.c	2006-01-03 20:21:00.000000000 +0100
@@ -63,6 +63,71 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fkeyslast;
+module_param_named(pb_fkeyslast, usbhid_pb_fkeyslast, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_fkeyslast, "Use F keys only while pressing fn on PowerBooks");
+
+static int usbhid_pb_disablefnkeys;
+module_param_named(pb_disablefnkeys, usbhid_pb_disablefnkeys, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablefnkeys, "Disable fn special keys on PowerBooks");
+
+static int usbhid_pb_disablekeypad;
+module_param_named(pb_disablekeypad, usbhid_pb_disablekeypad, bool, 0644);
+MODULE_PARM_DESC(usbhid_pb_disablekeypad, "Disable keypad keys on PowerBooks");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +138,85 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (!usbhid_pb_disablefnkeys) {
+		trans = find_translation(powerbook_fn_keys, usage->code);
+
+		if (trans) {
+			int do_translate;
+
+			if (test_bit(usage->code, hid->pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(!usbhid_pb_fkeyslast &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					( usbhid_pb_fkeyslast && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_fn);
+				else
+					clear_bit(usage->code, hid->pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+	}
+
+	if (!usbhid_pb_disablekeypad) {
+		int try_translate =
+			test_bit(usage->code, hid->pb_numlock)?1:
+			test_bit(LED_NUML, input->led);
+
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_numlock);
+				else
+					clear_bit(usage->code, hid->pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +469,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +646,11 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value)) {
+		return;
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -rNup linux-2.6.15-rc7.orig/drivers/usb/input/hid.h linux-2.6.15-rc7/drivers/usb/input/hid.h
--- linux-2.6.15-rc7.orig/drivers/usb/input/hid.h	2006-01-01 00:41:15.000000000 +0100
+++ linux-2.6.15-rc7/drivers/usb/input/hid.h	2006-01-03 00:45:13.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,14 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	/* We do this here because it's only relevant for the
+	 * USB devices, not for all input_dev's.
+	 */
+	unsigned long pb_fn[NBITS(KEY_MAX)];
+	unsigned long pb_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:43               ` Michael Hanselmann
@ 2006-01-11 21:47                 ` Vojtech Pavlik
  2006-01-11 21:50                   ` Michael Hanselmann
  2006-01-11 21:54                 ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 46+ messages in thread
From: Vojtech Pavlik @ 2006-01-11 21:47 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Benjamin Herrenschmidt, dtor_core, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Wed, Jan 11, 2006 at 10:43:51PM +0100, Michael Hanselmann wrote:

> On Thu, Jan 12, 2006 at 08:41:08AM +1100, Benjamin Herrenschmidt wrote:
> > > Johannes Berg told me he wants to use the fn key alone to switch the
> > > keyboard layout or something. For such uses, the pb_enablefn is there.
> 
> Sorry, actually it's called pb_disablefn now.
> 
> > What does it do ? Just send a keycode ? That should be unconditionnal.
> > The Fn key should change a keycode always. I don't see why you would
> > that to be off.
> 
> No, if that parameter is disabled, it translates key combinations like
> Fn+F1 to KEY_BRIGHTNESSUP. If it's enabled, it only sends KEY_FN.
 
I believe a better behavior would be to send KEY_FN and then
KEY_BRIGHTNESSUP when enabled and KEY_FN and KEY_F1 when disabled.

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:47                 ` Vojtech Pavlik
@ 2006-01-11 21:50                   ` Michael Hanselmann
  0 siblings, 0 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 21:50 UTC (permalink / raw)
  To: Vojtech Pavlik
  Cc: Benjamin Herrenschmidt, dtor_core, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Wed, Jan 11, 2006 at 10:47:32PM +0100, Vojtech Pavlik wrote:
> > No, if that parameter is disabled, it translates key combinations like
> > Fn+F1 to KEY_BRIGHTNESSUP. If it's enabled, it only sends KEY_FN.

> I believe a better behavior would be to send KEY_FN and then
> KEY_BRIGHTNESSUP when enabled and KEY_FN and KEY_F1 when disabled.

That's done. It sends KEY_FN as any other key. If a key is pressed while
the fn key is pressed, it uses the table to look up the new code.
Releasing is also done with the corresponding keycodes.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:43               ` Michael Hanselmann
  2006-01-11 21:47                 ` Vojtech Pavlik
@ 2006-01-11 21:54                 ` Benjamin Herrenschmidt
  1 sibling, 0 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-11 21:54 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Wed, 2006-01-11 at 22:43 +0100, Michael Hanselmann wrote:
> On Thu, Jan 12, 2006 at 08:41:08AM +1100, Benjamin Herrenschmidt wrote:
> > > Johannes Berg told me he wants to use the fn key alone to switch the
> > > keyboard layout or something. For such uses, the pb_enablefn is there.
> 
> Sorry, actually it's called pb_disablefn now.
> 
> > What does it do ? Just send a keycode ? That should be unconditionnal.
> > The Fn key should change a keycode always. I don't see why you would
> > that to be off.
> 
> No, if that parameter is disabled, it translates key combinations like
> Fn+F1 to KEY_BRIGHTNESSUP. If it's enabled, it only sends KEY_FN.

But that should be the full translation no ? I don't see why you are
splitting in translating the special keys and translating the rest of
the keyboard...

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 21:07     ` Dmitry Torokhov
  2006-01-11 21:20       ` Michael Hanselmann
  2006-01-11 21:30       ` Benjamin Herrenschmidt
@ 2006-01-11 23:26       ` Michael Hanselmann
  2006-01-11 23:41         ` Benjamin Herrenschmidt
  2 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-11 23:26 UTC (permalink / raw)
  To: dtor_core
  Cc: Benjamin Herrenschmidt, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Wed, Jan 11, 2006 at 04:07:37PM -0500, Dmitry Torokhov wrote:
> Ok, I am looking at the patch again, and I have a question - do we
> really need these 3 module parameters? If the goal is to be compatible
> with older keyboards then shouldn't we stick to one behavior?

After clearing the other points with Benjamin Herrenschmidt, I created
another patch which you find below.

This patch implements support for the fn key on PowerBooks using USB
based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-core.c	2006-01-08 11:53:36.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -upr linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid.h	2006-01-08 11:53:36.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,14 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	/* We do this here because it's only relevant for the
+	 * USB devices, not for all input_dev's.
+	 */
+	unsigned long pb_fn[NBITS(KEY_MAX)];
+	unsigned long pb_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-input.c	2006-01-11 23:41:14.000000000 +0100
@@ -63,6 +63,65 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(usbhid_pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, "
+	"1 = fkeyslast, 2 = fkeysfirst)");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +132,80 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int try_translate, do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, hid->pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_fn);
+				else
+					clear_bit(usage->code, hid->pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		try_translate = test_bit(usage->code, hid->pb_numlock)?1:
+				test_bit(LED_NUML, input->led);
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_numlock);
+				else
+					clear_bit(usage->code, hid->pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +458,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +635,11 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value)) {
+		return;
+	}
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 23:26       ` Michael Hanselmann
@ 2006-01-11 23:41         ` Benjamin Herrenschmidt
  2006-01-12  0:08           ` Michael Hanselmann
  2006-01-12  9:07           ` Vojtech Pavlik
  0 siblings, 2 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-11 23:41 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Thu, 2006-01-12 at 00:26 +0100, Michael Hanselmann wrote:

>   * This is the global environment of the parser. This information is
> @@ -431,6 +433,14 @@ struct hid_device {							/* device repo
>  	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
>  	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
>  			unsigned int type, unsigned int code, int value);
> +
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +	/* We do this here because it's only relevant for the
> +	 * USB devices, not for all input_dev's.
> +	 */
> +	unsigned long pb_fn[NBITS(KEY_MAX)];
> +	unsigned long pb_numlock[NBITS(KEY_MAX)];
> +#endif
>  };

I don't understand the comment above ? You are adding this to all struct
hid_device ? There can be only one of those keyboards plugged at one
point in time, I don't think there is any problem having the above
static in the driver rather than in the hid_device structure.

  .../...

>  
> +	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
> +	    hidinput_pb_event(hid, input, usage, value)) {
> +		return;
> +	}
> +

Dimitry might disagree but it's generally considered bad taste to have
{ and } for a single statement :)

>  	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
>  		int hat_dir = usage->hat_dir;
>  		if (!hat_dir)
> diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
> --- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
> +++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
> @@ -37,6 +37,16 @@ config USB_HIDINPUT
>  
>  	  If unsure, say Y.
>  
> +config USB_HIDINPUT_POWERBOOK
> +	bool "Enable support for iBook/PowerBook special keys"
> +	default n
> +	depends on USB_HIDINPUT
> +	help
> +	  Say Y here if you want support for the special keys (Fn, Numlock) on
> +	  Apple iBooks and PowerBooks.
> +
> +	  If unsure, say N.
> +
>  config HID_FF
>  	bool "Force feedback support (EXPERIMENTAL)"
>  	depends on USB_HIDINPUT && EXPERIMENTAL


^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 23:41         ` Benjamin Herrenschmidt
@ 2006-01-12  0:08           ` Michael Hanselmann
  2006-01-13  4:12             ` Dmitry Torokhov
  2006-01-12  9:07           ` Vojtech Pavlik
  1 sibling, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-12  0:08 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: dtor_core, linux-kernel, linux-input, linuxppc-dev, linux-kernel,
	Vojtech Pavlik

On Thu, Jan 12, 2006 at 10:41:40AM +1100, Benjamin Herrenschmidt wrote:
> I don't understand the comment above ? You are adding this to all struct
> hid_device ? There can be only one of those keyboards plugged at one
> point in time, I don't think there is any problem having the above
> static in the driver rather than in the hid_device structure.

While I don't agree on that, I still modified the patch.

This patch implements support for the fn key on Apple PowerBooks using
USB based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-core.c	2006-01-08 11:53:36.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -upr linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid.h	2006-01-12 00:56:22.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-input.c	2006-01-12 01:06:46.000000000 +0100
@@ -63,6 +63,68 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static unsigned long usbhid_pb_fn[NBITS(KEY_MAX)];
+static unsigned long usbhid_pb_numlock[NBITS(KEY_MAX)];
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(usbhid_pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, "
+	"1 = fkeyslast, 2 = fkeysfirst)");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +135,80 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int try_translate, do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, usbhid_pb_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, usbhid_pb_fn);
+				else
+					clear_bit(usage->code, usbhid_pb_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
+				test_bit(LED_NUML, input->led);
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, usbhid_pb_numlock);
+				else
+					clear_bit(usage->code, usbhid_pb_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +461,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +638,10 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value))
+		return;
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-11 23:41         ` Benjamin Herrenschmidt
  2006-01-12  0:08           ` Michael Hanselmann
@ 2006-01-12  9:07           ` Vojtech Pavlik
  2006-01-12 23:39             ` Michael Hanselmann
  1 sibling, 1 reply; 46+ messages in thread
From: Vojtech Pavlik @ 2006-01-12  9:07 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Michael Hanselmann, dtor_core, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Thu, Jan 12, 2006 at 10:41:40AM +1100, Benjamin Herrenschmidt wrote:
> On Thu, 2006-01-12 at 00:26 +0100, Michael Hanselmann wrote:
> 
> >   * This is the global environment of the parser. This information is
> > @@ -431,6 +433,14 @@ struct hid_device {							/* device repo
> >  	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
> >  	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
> >  			unsigned int type, unsigned int code, int value);
> > +
> > +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> > +	/* We do this here because it's only relevant for the
> > +	 * USB devices, not for all input_dev's.
> > +	 */
> > +	unsigned long pb_fn[NBITS(KEY_MAX)];
> > +	unsigned long pb_numlock[NBITS(KEY_MAX)];
> > +#endif
> >  };
> 
> I don't understand the comment above ? You are adding this to all struct
> hid_device ? There can be only one of those keyboards plugged at one
> point in time, I don't think there is any problem having the above
> static in the driver rather than in the hid_device structure.

I think having it in struct hid_device is safer. We might want to
dynamically allocate only for PowerBook keyboards, though, to save
memory.

> 
>   .../...
> 
> >  
> > +	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
> > +	    hidinput_pb_event(hid, input, usage, value)) {
> > +		return;
> > +	}
> > +
> 
> Dimitry might disagree but it's generally considered bad taste to have
> { and } for a single statement :)

I do agree, though.


-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-12  9:07           ` Vojtech Pavlik
@ 2006-01-12 23:39             ` Michael Hanselmann
  2006-01-13  1:53               ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-12 23:39 UTC (permalink / raw)
  To: Vojtech Pavlik
  Cc: Benjamin Herrenschmidt, dtor_core, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Thu, Jan 12, 2006 at 10:07:33AM +0100, Vojtech Pavlik wrote:
> I think having it in struct hid_device is safer. We might want to
> dynamically allocate only for PowerBook keyboards, though, to save
> memory.

These two arrays take 128 Bytes. I don't think it's possible to write
the code to allocate and set them in such short code. Beside of that, it
would affect more code than only hid-input.c, at least hid-core.c would
need modifications, too.

Benjamin Herrenschmidt had the idea of a private field which each device
can use for its own purposes. That's a task for another patch, tough.

This patch implements support for the fn key on Apple PowerBooks using
USB based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-core.c	2006-01-08 11:53:36.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -upr linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid.h	2006-01-13 00:15:10.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,11 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
+	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-input.c	2006-01-13 00:25:57.000000000 +0100
@@ -63,6 +63,65 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(usbhid_pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, "
+	"1 = fkeyslast, 2 = fkeysfirst)");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +132,80 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static inline struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table; trans->from && (trans->from != from); trans++);
+
+	return (trans->from?trans:NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int try_translate, do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, hid->pb_pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_fn);
+				else
+					clear_bit(usage->code, hid->pb_pressed_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		try_translate = test_bit(usage->code, hid->pb_pressed_numlock)?1:
+				test_bit(LED_NUML, input->led);
+		if (try_translate) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_numlock);
+				else
+					clear_bit(usage->code, hid->pb_pressed_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +458,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +635,10 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value))
+		return;
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-12 23:39             ` Michael Hanselmann
@ 2006-01-13  1:53               ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-13  1:53 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Vojtech Pavlik, dtor_core, linux-kernel, linux-input,
	linuxppc-dev, linux-kernel

On Fri, 2006-01-13 at 00:39 +0100, Michael Hanselmann wrote:
> On Thu, Jan 12, 2006 at 10:07:33AM +0100, Vojtech Pavlik wrote:
> > I think having it in struct hid_device is safer. We might want to
> > dynamically allocate only for PowerBook keyboards, though, to save
> > memory.
> 
> These two arrays take 128 Bytes. I don't think it's possible to write
> the code to allocate and set them in such short code. Beside of that, it
> would affect more code than only hid-input.c, at least hid-core.c would
> need modifications, too.
> 
> Benjamin Herrenschmidt had the idea of a private field which each device
> can use for its own purposes. That's a task for another patch, tough.
> 
> This patch implements support for the fn key on Apple PowerBooks using
> USB based keyboards.

Dimitri, I think the patch is good enough now and should go in for
2.6.16.

Thanks !
Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-12  0:08           ` Michael Hanselmann
@ 2006-01-13  4:12             ` Dmitry Torokhov
  2006-01-13  6:53               ` Michael Hanselmann
  2006-01-13 21:55               ` Benjamin Herrenschmidt
  0 siblings, 2 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-13  4:12 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Benjamin Herrenschmidt, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Wednesday 11 January 2006 19:08, Michael Hanselmann wrote:
> On Thu, Jan 12, 2006 at 10:41:40AM +1100, Benjamin Herrenschmidt wrote:
> > I don't understand the comment above ? You are adding this to all struct
> > hid_device ? There can be only one of those keyboards plugged at one
> > point in time, I don't think there is any problem having the above
> > static in the driver rather than in the hid_device structure.
> 
> While I don't agree on that, I still modified the patch.
>

I disagree as well, but if we get semantics right we can merge it in
this form and adjust later. 
 
> +static int usbhid_pb_fnmode = 1;
> +module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
> +MODULE_PARM_DESC(usbhid_pb_fnmode,
> +	"Mode of fn key on PowerBooks (0 = disabled, "
> +	"1 = fkeyslast, 2 = fkeysfirst)");
> +#endif
> +

That should be "MODULE_PARM_DESC(pb_fn_mode, ...)". Also, since this is
for compatibility with ADB, why do we have 3 options? Doesn't ADB have
only 2?

>  #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
>  #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
>  #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
> @@ -73,6 +135,80 @@ static struct {
>  #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
>  #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
>  
> +static inline struct hidinput_key_translation *find_translation(

I thought is was agreed that we'd avoid "inlines" in .c files?

> +	struct hidinput_key_translation *table, u16 from)
> +{
> +	struct hidinput_key_translation *trans;
> +
> +	/* Look for the translation */
> +	for(trans = table; trans->from && (trans->from != from); trans++);
> +
> +	return (trans->from?trans:NULL);
> +}

I'd prefer liberal amount of spaces applied here </extreme nitpick mode>

> +
> +static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
> +	struct hid_usage *usage, __s32 value)
> +{
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +	struct hidinput_key_translation *trans;
> +
> +	if (usage->code == KEY_FN) {
> +		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
> +		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
> +
> +		input_event(input, usage->type, usage->code, value);
> +
> +		return 1;
> +	}
> +
> +	if (usbhid_pb_fnmode) {
> +		int try_translate, do_translate;
> +
> +		trans = find_translation(powerbook_fn_keys, usage->code);
> +		if (trans) {
> +			if (test_bit(usage->code, usbhid_pb_fn))
> +				do_translate = 1;
> +			else if (trans->flags & POWERBOOK_FLAG_FKEY)
> +				do_translate =
> +					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
> +					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
> +			else
> +				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
> +
> +			if (do_translate) {
> +				if (value)
> +					set_bit(usage->code, usbhid_pb_fn);
> +				else
> +					clear_bit(usage->code, usbhid_pb_fn);
> +
> +				input_event(input, usage->type, trans->to, value);
> +
> +				return 1;
> +			}
> +		}
> +
> +		try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> +				test_bit(LED_NUML, input->led);
> +		if (try_translate) {

Isn't this the same as 

		if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))

but harder to read?

-- 
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13  4:12             ` Dmitry Torokhov
@ 2006-01-13  6:53               ` Michael Hanselmann
  2006-01-13  7:47                 ` Vojtech Pavlik
  2006-01-13 21:55               ` Benjamin Herrenschmidt
  1 sibling, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-13  6:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Benjamin Herrenschmidt, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Thu, Jan 12, 2006 at 11:12:04PM -0500, Dmitry Torokhov wrote:
> Also, since this is for compatibility with ADB, why do we have 3
> options? Doesn't ADB have only 2?

Some people want to use the fn key without having the translations. I'm
not sure wether the ADB hardware even supported this. So far, it's at
least not useless.

> I thought is was agreed that we'd avoid "inlines" in .c files?

I wasn't sure if that all got trough yet.

> > +	return (trans->from?trans:NULL);

> I'd prefer liberal amount of spaces applied here </extreme nitpick mode>

Hopefully fixed in a Dmitry-compatible way. :-)

> > +		try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> > +				test_bit(LED_NUML, input->led);
> > +		if (try_translate) {

> Isn't this the same as 

> 		if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))

> but harder to read?

It indeed is. Fixed.

New patch:

This patch implements support for the fn key on Apple PowerBooks using
USB based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-core.c	2006-01-08 11:53:36.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -upr linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid.h	2006-01-13 00:15:10.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,11 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
+	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-input.c	2006-01-13 07:45:00.000000000 +0100
@@ -63,6 +63,65 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, "
+	"1 = fkeyslast, 2 = fkeysfirst)");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +132,81 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table;
+	    trans->from && (trans->from != from);
+	    trans++);
+
+	return (trans->from? trans : NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, hid->pb_pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_fn);
+				else
+					clear_bit(usage->code, hid->pb_pressed_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		if (test_bit(usage->code, hid->pb_pressed_numlock) ||
+		    test_bit(LED_NUML, input->led)) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_numlock);
+				else
+					clear_bit(usage->code, hid->pb_pressed_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +459,27 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+
+					set_bit(KEY_FN, input->keybit);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					goto ignore;
+				}
+#endif
+
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +636,10 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value))
+		return;
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13  6:53               ` Michael Hanselmann
@ 2006-01-13  7:47                 ` Vojtech Pavlik
  2006-01-13 22:02                   ` Michael Hanselmann
  0 siblings, 1 reply; 46+ messages in thread
From: Vojtech Pavlik @ 2006-01-13  7:47 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Dmitry Torokhov, Benjamin Herrenschmidt, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel

On Fri, Jan 13, 2006 at 07:53:02AM +0100, Michael Hanselmann wrote:
 
> It indeed is. Fixed.

It gets better all the time. ;)

> New patch:
> 
> This patch implements support for the fn key on Apple PowerBooks using
> USB based keyboards.
> 
> Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
> Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
> Acked-by: Johannes Berg <johannes@sipsolutions.net>
> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> ---

> @@ -325,7 +459,27 @@ static void hidinput_configure_usage(str
>  
>  			set_bit(EV_REP, input->evbit);
>  			switch(usage->hid & HID_USAGE) {
> -				case 0x003: map_key_clear(KEY_FN);		break;
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +				/* The fn key on Apple PowerBooks */
> +				case 0x0003: {
> +					struct hidinput_key_translation *trans;
> +
> +					map_key_clear(KEY_FN);
> +
> +					set_bit(KEY_FN, input->keybit);

The set_bit(KEY_FN, input->keybit) is superfluous here, right?
map_key_clear(KEY_FN); will take care of that further down.

> +					set_bit(KEY_NUMLOCK, input->keybit);
> +
> +					/* Enable all needed keys */
> +					for(trans = powerbook_fn_keys; trans->from; trans++)
> +						set_bit(trans->to, input->keybit);
> +
> +					for(trans = powerbook_numlock_keys; trans->from; trans++)
> +						set_bit(trans->to, input->keybit);
> +
> +					goto ignore;
> +				}
> +#endif
> +
>  				default:    goto ignore;
>  			}
>  			break;

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13  4:12             ` Dmitry Torokhov
  2006-01-13  6:53               ` Michael Hanselmann
@ 2006-01-13 21:55               ` Benjamin Herrenschmidt
  2006-01-13 21:57                 ` Benjamin Herrenschmidt
  2006-01-13 22:05                 ` Dmitry Torokhov
  1 sibling, 2 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-13 21:55 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik


> That should be "MODULE_PARM_DESC(pb_fn_mode, ...)". Also, since this is
> for compatibility with ADB, why do we have 3 options? Doesn't ADB have
> only 2?

No, the ADB keyboard can operate in 2 modes that can be set with a PMU
command, I forgot about that in my earlier comments. In one mode, you get
the "special" behaviour by default on the Fx keys and you get Fx when
pressing Fn-Fx, and in the other mode, you get the Fx by default and the
special behaviour when pressing Fn-Fx.

> > +static inline struct hidinput_key_translation *find_translation(
> 
> I thought is was agreed that we'd avoid "inlines" in .c files?

Ah ? I have certainly missed that discussion ...

> > +	struct hidinput_key_translation *table, u16 from)
> > +{
> > +	struct hidinput_key_translation *trans;
> > +
> > +	/* Look for the translation */
> > +	for(trans = table; trans->from && (trans->from != from); trans++);
> > +
> > +	return (trans->from?trans:NULL);
> > +}
> 
> I'd prefer liberal amount of spaces applied here </extreme nitpick mode>

Me too :)

> > +		try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> > +				test_bit(LED_NUML, input->led);
> > +		if (try_translate) {
> 
> Isn't this the same as 
> 
> 		if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))
> 
> but harder to read?

No. If the first one is 0, the second one will not matter in the first
version, while it will in yours.

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 21:55               ` Benjamin Herrenschmidt
@ 2006-01-13 21:57                 ` Benjamin Herrenschmidt
  2006-01-13 22:05                 ` Dmitry Torokhov
  1 sibling, 0 replies; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-13 21:57 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On Sat, 2006-01-14 at 08:55 +1100, Benjamin Herrenschmidt wrote:

> 
> > > +		try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> > > +				test_bit(LED_NUML, input->led);
> > > +		if (try_translate) {
> > 
> > Isn't this the same as 
> > 
> > 		if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))
> > 
> > but harder to read?
> 
> No. If the first one is 0, the second one will not matter in the first
> version, while it will in yours.

Forget me, I'm stupid or rather I should finish by breakfast before
writing stupid things ;) Of course you are right, they are equivalent.

Ben.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13  7:47                 ` Vojtech Pavlik
@ 2006-01-13 22:02                   ` Michael Hanselmann
  2006-01-14  4:58                     ` Dmitry Torokhov
  0 siblings, 1 reply; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-13 22:02 UTC (permalink / raw)
  To: Vojtech Pavlik
  Cc: Dmitry Torokhov, Benjamin Herrenschmidt, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel

On Fri, Jan 13, 2006 at 08:47:49AM +0100, Vojtech Pavlik wrote:
> > +					map_key_clear(KEY_FN);
> > +					set_bit(KEY_FN, input->keybit);

> The set_bit(KEY_FN, input->keybit) is superfluous here, right?
> map_key_clear(KEY_FN); will take care of that further down.

Okay, fixed in the patch below.

This patch implements support for the fn key on Apple PowerBooks using
USB based keyboards.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

---
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-core.c linux-2.6.15/drivers/usb/input/hid-core.c
--- linux-2.6.15.orig/drivers/usb/input/hid-core.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-core.c	2006-01-08 11:53:36.000000000 +0100
@@ -1580,6 +1580,14 @@ static struct hid_blacklist {
 	{ USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD },
 	{ USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
diff -upr linux-2.6.15.orig/drivers/usb/input/hid.h linux-2.6.15/drivers/usb/input/hid.h
--- linux-2.6.15.orig/drivers/usb/input/hid.h	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid.h	2006-01-13 00:15:10.000000000 +0100
@@ -246,6 +246,8 @@ struct hid_item {
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
 #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
 #define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00000800
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00001000
 
 /*
  * This is the global environment of the parser. This information is
@@ -431,6 +433,11 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
+	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
diff -upr linux-2.6.15.orig/drivers/usb/input/hid-input.c linux-2.6.15/drivers/usb/input/hid-input.c
--- linux-2.6.15.orig/drivers/usb/input/hid-input.c	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/hid-input.c	2006-01-13 22:45:21.000000000 +0100
@@ -63,6 +63,65 @@ static struct {
 	__s32 y;
 }  hid_hat_to_axis[] = {{ 0, 0}, { 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}};
 
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, "
+	"1 = fkeyslast, 2 = fkeysfirst)");
+#endif
+
 #define map_abs(c)	do { usage->code = c; usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; } while (0)
 #define map_rel(c)	do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
 #define map_key(c)	do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
@@ -73,6 +132,81 @@ static struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+static struct hidinput_key_translation *find_translation(
+	struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for(trans = table;
+	    trans->from && (trans->from != from);
+	    trans++);
+
+	return (trans->from? trans : NULL);
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+	struct hid_usage *usage, __s32 value)
+{
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, hid->pb_pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_fn);
+				else
+					clear_bit(usage->code, hid->pb_pressed_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		if (test_bit(usage->code, hid->pb_pressed_numlock) ||
+		    test_bit(LED_NUML, input->led)) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_numlock);
+				else
+					clear_bit(usage->code, hid->pb_pressed_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+#endif
+
+	return 0;
+}
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -325,7 +459,24 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+				/* The fn key on Apple PowerBooks */
+				case 0x0003: {
+					struct hidinput_key_translation *trans;
+
+					map_key_clear(KEY_FN);
+					set_bit(KEY_NUMLOCK, input->keybit);
+
+					/* Enable all needed keys */
+					for(trans = powerbook_fn_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					for(trans = powerbook_numlock_keys; trans->from; trans++)
+						set_bit(trans->to, input->keybit);
+
+					break;
+				}
+#endif
 				default:    goto ignore;
 			}
 			break;
@@ -482,6 +633,10 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) &&
+	    hidinput_pb_event(hid, input, usage, value))
+		return;
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
diff -upr linux-2.6.15.orig/drivers/usb/input/Kconfig linux-2.6.15/drivers/usb/input/Kconfig
--- linux-2.6.15.orig/drivers/usb/input/Kconfig	2006-01-11 23:59:40.000000000 +0100
+++ linux-2.6.15/drivers/usb/input/Kconfig	2006-01-08 11:53:35.000000000 +0100
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 21:55               ` Benjamin Herrenschmidt
  2006-01-13 21:57                 ` Benjamin Herrenschmidt
@ 2006-01-13 22:05                 ` Dmitry Torokhov
  2006-01-13 22:08                   ` Dmitry Torokhov
  2006-01-13 22:14                   ` Benjamin Herrenschmidt
  1 sibling, 2 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-13 22:05 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On 1/13/06, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
>
> > That should be "MODULE_PARM_DESC(pb_fn_mode, ...)". Also, since this is
> > for compatibility with ADB, why do we have 3 options? Doesn't ADB have
> > only 2?
>
> No, the ADB keyboard can operate in 2 modes that can be set with a PMU
> command, I forgot about that in my earlier comments. In one mode, you get
> the "special" behaviour by default on the Fx keys and you get Fx when
> pressing Fn-Fx, and in the other mode, you get the Fx by default and the
> special behaviour when pressing Fn-Fx.
>

Right, so do we need "no translation, fnkeyfirst and fnkeylast" option
or just "fnkeyfirst and fnkeyast"?

> > > +static inline struct hidinput_key_translation *find_translation(
> >
> > I thought is was agreed that we'd avoid "inlines" in .c files?
>
> Ah ? I have certainly missed that discussion ...

Newer GCCs, unit-at-a-time, etc. etc. - teher was pretty long
discussion about letting GCC decide on inlining.

>
> > > +   struct hidinput_key_translation *table, u16 from)
> > > +{
> > > +   struct hidinput_key_translation *trans;
> > > +
> > > +   /* Look for the translation */
> > > +   for(trans = table; trans->from && (trans->from != from); trans++);
> > > +
> > > +   return (trans->from?trans:NULL);
> > > +}
> >
> > I'd prefer liberal amount of spaces applied here </extreme nitpick mode>
>
> Me too :)
>
> > > +           try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> > > +                           test_bit(LED_NUML, input->led);
> > > +           if (try_translate) {
> >
> > Isn't this the same as
> >
> >               if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))
> >
> > but harder to read?
>
> No. If the first one is 0, the second one will not matter in the first
> version, while it will in yours.
>

Huh? You mean 1, right?

    try_translate = 0;
    if (test_bit(usage->code, usbhid_pb_numlock))
         try_translate = 1;
    else if (test_bit(LED_NUML, input->led))
         try_translate = 1;
    else







> Ben.
>
>
>


--
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 22:05                 ` Dmitry Torokhov
@ 2006-01-13 22:08                   ` Dmitry Torokhov
  2006-01-13 22:14                   ` Benjamin Herrenschmidt
  1 sibling, 0 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-13 22:08 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On 1/13/06, Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote:
> On 1/13/06, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> >
> > > That should be "MODULE_PARM_DESC(pb_fn_mode, ...)". Also, since this is
> > > for compatibility with ADB, why do we have 3 options? Doesn't ADB have
> > > only 2?
> >
> > No, the ADB keyboard can operate in 2 modes that can be set with a PMU
> > command, I forgot about that in my earlier comments. In one mode, you get
> > the "special" behaviour by default on the Fx keys and you get Fx when
> > pressing Fn-Fx, and in the other mode, you get the Fx by default and the
> > special behaviour when pressing Fn-Fx.
> >
>
> Right, so do we need "no translation, fnkeyfirst and fnkeylast" option
> or just "fnkeyfirst and fnkeyast"?
>
> > > > +static inline struct hidinput_key_translation *find_translation(
> > >
> > > I thought is was agreed that we'd avoid "inlines" in .c files?
> >
> > Ah ? I have certainly missed that discussion ...
>
> Newer GCCs, unit-at-a-time, etc. etc. - teher was pretty long
> discussion about letting GCC decide on inlining.
>
> >
> > > > +   struct hidinput_key_translation *table, u16 from)
> > > > +{
> > > > +   struct hidinput_key_translation *trans;
> > > > +
> > > > +   /* Look for the translation */
> > > > +   for(trans = table; trans->from && (trans->from != from); trans++);
> > > > +
> > > > +   return (trans->from?trans:NULL);
> > > > +}
> > >
> > > I'd prefer liberal amount of spaces applied here </extreme nitpick mode>
> >
> > Me too :)
> >
> > > > +           try_translate = test_bit(usage->code, usbhid_pb_numlock)?1:
> > > > +                           test_bit(LED_NUML, input->led);
> > > > +           if (try_translate) {
> > >
> > > Isn't this the same as
> > >
> > >               if (test_bit(usage->code, usbhid_pb_numlock) || test_bit(LED_NUML, input->led))
> > >
> > > but harder to read?
> >
> > No. If the first one is 0, the second one will not matter in the first
> > version, while it will in yours.
> >
>
> Huh? You mean 1, right?
>
>    try_translate = 0;
>    if (test_bit(usage->code, usbhid_pb_numlock))
>         try_translate = 1;
>    else if (test_bit(LED_NUML, input->led))
>         try_translate = 1;
>    else
>

Uhg, accidentially hit send, sorry... Ok, nevermind, I see your other message ;)

--
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 22:05                 ` Dmitry Torokhov
  2006-01-13 22:08                   ` Dmitry Torokhov
@ 2006-01-13 22:14                   ` Benjamin Herrenschmidt
  2006-01-13 22:25                     ` Dmitry Torokhov
  1 sibling, 1 reply; 46+ messages in thread
From: Benjamin Herrenschmidt @ 2006-01-13 22:14 UTC (permalink / raw)
  To: dtor_core
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

> Right, so do we need "no translation, fnkeyfirst and fnkeylast" option
> or just "fnkeyfirst and fnkeyast"?

I think "no translation" should still be around if people want to handle
it entirely from userland no ?

That is:

 - no translation : nothing special is done, Fx sends Fx keycode
regardless of Fn key, Fn key itsef sends a keycode for itself, there is
no emulation of numlock

 - fnkeyfirst / fnkeylast : Either Fx is translated and Fn-Fx is not or
the opposite. Numlock emulation is enabled.

> Huh? You mean 1, right?

Yah, forget it, I was on crack.



^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 22:14                   ` Benjamin Herrenschmidt
@ 2006-01-13 22:25                     ` Dmitry Torokhov
  0 siblings, 0 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-13 22:25 UTC (permalink / raw)
  To: Benjamin Herrenschmidt
  Cc: Michael Hanselmann, linux-kernel, linux-input, linuxppc-dev,
	linux-kernel, Vojtech Pavlik

On 1/13/06, Benjamin Herrenschmidt <benh@kernel.crashing.org> wrote:
> > Right, so do we need "no translation, fnkeyfirst and fnkeylast" option
> > or just "fnkeyfirst and fnkeyast"?
>
> I think "no translation" should still be around if people want to handle
> it entirely from userland no ?
>
> That is:
>
>  - no translation : nothing special is done, Fx sends Fx keycode
> regardless of Fn key, Fn key itsef sends a keycode for itself, there is
> no emulation of numlock
>
>  - fnkeyfirst / fnkeylast : Either Fx is translated and Fn-Fx is not or
> the opposite. Numlock emulation is enabled.

OK then, I will push the patch to Linus.

--
Dmitry

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-13 22:02                   ` Michael Hanselmann
@ 2006-01-14  4:58                     ` Dmitry Torokhov
  2006-01-14 10:41                       ` Vojtech Pavlik
  2006-01-14 10:57                       ` Michael Hanselmann
  0 siblings, 2 replies; 46+ messages in thread
From: Dmitry Torokhov @ 2006-01-14  4:58 UTC (permalink / raw)
  To: Michael Hanselmann
  Cc: Vojtech Pavlik, Benjamin Herrenschmidt, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel

On Friday 13 January 2006 17:02, Michael Hanselmann wrote:
> -                               case 0x003: map_key_clear(KEY_FN);              break;
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +                               /* The fn key on Apple PowerBooks */
> +                               case 0x0003: {
> +                                       struct hidinput_key_translation *trans;
> +
> +                                       map_key_clear(KEY_FN);
> +                                       set_bit(KEY_NUMLOCK, input->keybit);
> +

One little thing - I think that we should report FN key even if PowerBook
support is disabled. Can I solicit input on the patch below (I also rearranged
teh code slightly)? 

-- 
Dmitry

From: Michael Hanselmann <linux-kernel@hansmi.ch>

Input: HID - add support for fn key on Apple PowerBooks

This patch implements support for the fn key on Apple PowerBooks using
USB based keyboards and makes them behave like their ADB counterparts.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
---

 drivers/usb/input/Kconfig     |   10 ++
 drivers/usb/input/hid-core.c  |    8 ++
 drivers/usb/input/hid-input.c |  166 +++++++++++++++++++++++++++++++++++++++++-
 drivers/usb/input/hid.h       |   31 ++++---
 4 files changed, 201 insertions(+), 14 deletions(-)

Index: work/drivers/usb/input/hid-core.c
===================================================================
--- work.orig/drivers/usb/input/hid-core.c
+++ work/drivers/usb/input/hid-core.c
@@ -1585,6 +1585,14 @@ static const struct hid_blacklist {
 
 	{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
 
+	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
+	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
+
 	{ 0, 0 }
 };
 
Index: work/drivers/usb/input/hid.h
===================================================================
--- work.orig/drivers/usb/input/hid.h
+++ work/drivers/usb/input/hid.h
@@ -235,18 +235,20 @@ struct hid_item {
  * HID device quirks.
  */
 
-#define HID_QUIRK_INVERT			0x001
-#define HID_QUIRK_NOTOUCH			0x002
-#define HID_QUIRK_IGNORE			0x004
-#define HID_QUIRK_NOGET				0x008
-#define HID_QUIRK_HIDDEV			0x010
-#define HID_QUIRK_BADPAD			0x020
-#define HID_QUIRK_MULTI_INPUT			0x040
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x080
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
-#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
-#define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
-#define HID_QUIRK_CYMOTION			0x800
+#define HID_QUIRK_INVERT			0x00000001
+#define HID_QUIRK_NOTOUCH			0x00000002
+#define HID_QUIRK_IGNORE			0x00000004
+#define HID_QUIRK_NOGET				0x00000008
+#define HID_QUIRK_HIDDEV			0x00000010
+#define HID_QUIRK_BADPAD			0x00000020
+#define HID_QUIRK_MULTI_INPUT			0x00000040
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x00000080
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x00000100
+#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x00000200
+#define HID_QUIRK_2WHEEL_POWERMOUSE		0x00000400
+#define HID_QUIRK_CYMOTION			0x00000800
+#define HID_QUIRK_POWERBOOK_HAS_FN		0x00001000
+#define HID_QUIRK_POWERBOOK_FN_ON		0x00002000
 
 /*
  * This is the global environment of the parser. This information is
@@ -432,6 +434,11 @@ struct hid_device {							/* device repo
 	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
 	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
 			unsigned int type, unsigned int code, int value);
+
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
+	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
+#endif
 };
 
 #define HID_GLOBAL_STACK_SIZE 4
Index: work/drivers/usb/input/hid-input.c
===================================================================
--- work.orig/drivers/usb/input/hid-input.c
+++ work/drivers/usb/input/hid-input.c
@@ -73,6 +73,160 @@ static const struct {
 #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
 #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
 
+#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
+
+struct hidinput_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+#define POWERBOOK_FLAG_FKEY 0x01
+
+static struct hidinput_key_translation powerbook_fn_keys[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
+	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
+	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_RIGHT,	KEY_END },
+	{ }
+};
+
+static struct hidinput_key_translation powerbook_numlock_keys[] = {
+	{ KEY_J,	KEY_KP1 },
+	{ KEY_K,	KEY_KP2 },
+	{ KEY_L,	KEY_KP3 },
+	{ KEY_U,	KEY_KP4 },
+	{ KEY_I,	KEY_KP5 },
+	{ KEY_O,	KEY_KP6 },
+	{ KEY_7,	KEY_KP7 },
+	{ KEY_8,	KEY_KP8 },
+	{ KEY_9,	KEY_KP9 },
+	{ KEY_M,	KEY_KP0 },
+	{ KEY_DOT,	KEY_KPDOT },
+	{ KEY_SLASH,	KEY_KPPLUS },
+	{ KEY_SEMICOLON, KEY_KPMINUS },
+	{ KEY_P,	KEY_KPASTERISK },
+	{ KEY_MINUS,	KEY_KPEQUAL },
+	{ KEY_0,	KEY_KPSLASH },
+	{ KEY_F6,	KEY_NUMLOCK },
+	{ KEY_KPENTER,	KEY_KPENTER },
+	{ KEY_BACKSPACE, KEY_BACKSPACE },
+	{ }
+};
+
+static int usbhid_pb_fnmode = 1;
+module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
+MODULE_PARM_DESC(pb_fnmode,
+	"Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
+
+static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
+{
+	struct hidinput_key_translation *trans;
+
+	/* Look for the translation */
+	for (trans = table; trans->from; trans++)
+		if (trans->from == from)
+			return trans;
+
+	return NULL;
+}
+
+static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+			     struct hid_usage *usage, __s32 value)
+{
+	struct hidinput_key_translation *trans;
+
+	if (usage->code == KEY_FN) {
+		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
+		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
+
+		input_event(input, usage->type, usage->code, value);
+
+		return 1;
+	}
+
+	if (usbhid_pb_fnmode) {
+		int do_translate;
+
+		trans = find_translation(powerbook_fn_keys, usage->code);
+		if (trans) {
+			if (test_bit(usage->code, hid->pb_pressed_fn))
+				do_translate = 1;
+			else if (trans->flags & POWERBOOK_FLAG_FKEY)
+				do_translate =
+					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
+					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
+			else
+				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
+
+			if (do_translate) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_fn);
+				else
+					clear_bit(usage->code, hid->pb_pressed_fn);
+
+				input_event(input, usage->type, trans->to, value);
+
+				return 1;
+			}
+		}
+
+		if (test_bit(usage->code, hid->pb_pressed_numlock) ||
+		    test_bit(LED_NUML, input->led)) {
+			trans = find_translation(powerbook_numlock_keys, usage->code);
+
+			if (trans) {
+				if (value)
+					set_bit(usage->code, hid->pb_pressed_numlock);
+				else
+					clear_bit(usage->code, hid->pb_pressed_numlock);
+
+				input_event(input, usage->type, trans->to, value);
+			}
+
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void hidinput_pb_setup(struct input_dev *input)
+{
+	struct hidinput_key_translation *trans;
+
+	set_bit(KEY_NUMLOCK, input->keybit);
+
+	/* Enable all needed keys */
+	for (trans = powerbook_fn_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+
+	for (trans = powerbook_numlock_keys; trans->from; trans++)
+		set_bit(trans->to, input->keybit);
+}
+#else
+static inline int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
+				    struct hid_usage *usage, __s32 value)
+{
+	return 0;
+}
+
+static inline void hidinput_pb_setup(struct input_dev *input)
+{
+}
+#endif
+
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
 				     struct hid_usage *usage)
 {
@@ -336,7 +490,12 @@ static void hidinput_configure_usage(str
 
 			set_bit(EV_REP, input->evbit);
 			switch(usage->hid & HID_USAGE) {
-				case 0x003: map_key_clear(KEY_FN);		break;
+				case 0x003:
+					/* The fn key on Apple PowerBooks */
+					map_key_clear(KEY_FN);
+					hidinput_pb_setup(input);
+					break;
+
 				default:    goto ignore;
 			}
 			break;
@@ -493,6 +652,9 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
+	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value))
+		return;
+
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
@@ -535,7 +697,7 @@ void hidinput_hid_event(struct hid_devic
 		return;
 	}
 
-	if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
+	if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
 		return;
 
 	input_event(input, usage->type, usage->code, value);
Index: work/drivers/usb/input/Kconfig
===================================================================
--- work.orig/drivers/usb/input/Kconfig
+++ work/drivers/usb/input/Kconfig
@@ -37,6 +37,16 @@ config USB_HIDINPUT
 
 	  If unsure, say Y.
 
+config USB_HIDINPUT_POWERBOOK
+	bool "Enable support for iBook/PowerBook special keys"
+	default n
+	depends on USB_HIDINPUT
+	help
+	  Say Y here if you want support for the special keys (Fn, Numlock) on
+	  Apple iBooks and PowerBooks.
+
+	  If unsure, say N.
+
 config HID_FF
 	bool "Force feedback support (EXPERIMENTAL)"
 	depends on USB_HIDINPUT && EXPERIMENTAL

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-14  4:58                     ` Dmitry Torokhov
@ 2006-01-14 10:41                       ` Vojtech Pavlik
  2006-01-14 10:57                       ` Michael Hanselmann
  1 sibling, 0 replies; 46+ messages in thread
From: Vojtech Pavlik @ 2006-01-14 10:41 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Michael Hanselmann, Benjamin Herrenschmidt, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel

On Fri, Jan 13, 2006 at 11:58:33PM -0500, Dmitry Torokhov wrote:
> On Friday 13 January 2006 17:02, Michael Hanselmann wrote:
> > -                               case 0x003: map_key_clear(KEY_FN);              break;
> > +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> > +                               /* The fn key on Apple PowerBooks */
> > +                               case 0x0003: {
> > +                                       struct hidinput_key_translation *trans;
> > +
> > +                                       map_key_clear(KEY_FN);
> > +                                       set_bit(KEY_NUMLOCK, input->keybit);
> > +
> 
> One little thing - I think that we should report FN key even if PowerBook
> support is disabled. Can I solicit input on the patch below (I also rearranged
> teh code slightly)? 

Looks fine. We might want to skip adding the keys to the bitmap if the
support is compiled in, but disabled, but then it wouldn't be possible
to enable it at runtime.

> 
> -- 
> Dmitry
> 
> From: Michael Hanselmann <linux-kernel@hansmi.ch>
> 
> Input: HID - add support for fn key on Apple PowerBooks
> 
> This patch implements support for the fn key on Apple PowerBooks using
> USB based keyboards and makes them behave like their ADB counterparts.
> 
> Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
> Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
> Acked-by: Johannes Berg <johannes@sipsolutions.net>
> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> 
> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
> ---
> 
>  drivers/usb/input/Kconfig     |   10 ++
>  drivers/usb/input/hid-core.c  |    8 ++
>  drivers/usb/input/hid-input.c |  166 +++++++++++++++++++++++++++++++++++++++++-
>  drivers/usb/input/hid.h       |   31 ++++---
>  4 files changed, 201 insertions(+), 14 deletions(-)
> 
> Index: work/drivers/usb/input/hid-core.c
> ===================================================================
> --- work.orig/drivers/usb/input/hid-core.c
> +++ work/drivers/usb/input/hid-core.c
> @@ -1585,6 +1585,14 @@ static const struct hid_blacklist {
>  
>  	{ USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION },
>  
> +	{ USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN },
> +	{ USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN },
> +
>  	{ 0, 0 }
>  };
>  
> Index: work/drivers/usb/input/hid.h
> ===================================================================
> --- work.orig/drivers/usb/input/hid.h
> +++ work/drivers/usb/input/hid.h
> @@ -235,18 +235,20 @@ struct hid_item {
>   * HID device quirks.
>   */
>  
> -#define HID_QUIRK_INVERT			0x001
> -#define HID_QUIRK_NOTOUCH			0x002
> -#define HID_QUIRK_IGNORE			0x004
> -#define HID_QUIRK_NOGET				0x008
> -#define HID_QUIRK_HIDDEV			0x010
> -#define HID_QUIRK_BADPAD			0x020
> -#define HID_QUIRK_MULTI_INPUT			0x040
> -#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x080
> -#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x100
> -#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x200
> -#define HID_QUIRK_2WHEEL_POWERMOUSE		0x400
> -#define HID_QUIRK_CYMOTION			0x800
> +#define HID_QUIRK_INVERT			0x00000001
> +#define HID_QUIRK_NOTOUCH			0x00000002
> +#define HID_QUIRK_IGNORE			0x00000004
> +#define HID_QUIRK_NOGET				0x00000008
> +#define HID_QUIRK_HIDDEV			0x00000010
> +#define HID_QUIRK_BADPAD			0x00000020
> +#define HID_QUIRK_MULTI_INPUT			0x00000040
> +#define HID_QUIRK_2WHEEL_MOUSE_HACK_7		0x00000080
> +#define HID_QUIRK_2WHEEL_MOUSE_HACK_5		0x00000100
> +#define HID_QUIRK_2WHEEL_MOUSE_HACK_ON		0x00000200
> +#define HID_QUIRK_2WHEEL_POWERMOUSE		0x00000400
> +#define HID_QUIRK_CYMOTION			0x00000800
> +#define HID_QUIRK_POWERBOOK_HAS_FN		0x00001000
> +#define HID_QUIRK_POWERBOOK_FN_ON		0x00002000
>  
>  /*
>   * This is the global environment of the parser. This information is
> @@ -432,6 +434,11 @@ struct hid_device {							/* device repo
>  	void (*ff_exit)(struct hid_device*);                            /* Called by hid_exit_ff(hid) */
>  	int (*ff_event)(struct hid_device *hid, struct input_dev *input,
>  			unsigned int type, unsigned int code, int value);
> +
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +	unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
> +	unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
> +#endif
>  };
>  
>  #define HID_GLOBAL_STACK_SIZE 4
> Index: work/drivers/usb/input/hid-input.c
> ===================================================================
> --- work.orig/drivers/usb/input/hid-input.c
> +++ work/drivers/usb/input/hid-input.c
> @@ -73,6 +73,160 @@ static const struct {
>  #define map_key_clear(c)	do { map_key(c); clear_bit(c, bit); } while (0)
>  #define map_ff_effect(c)	do { set_bit(c, input->ffbit); } while (0)
>  
> +#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
> +
> +struct hidinput_key_translation {
> +	u16 from;
> +	u16 to;
> +	u8 flags;
> +};
> +
> +#define POWERBOOK_FLAG_FKEY 0x01
> +
> +static struct hidinput_key_translation powerbook_fn_keys[] = {
> +	{ KEY_BACKSPACE, KEY_DELETE },
> +	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	POWERBOOK_FLAG_FKEY },
> +	{ KEY_F2,	KEY_BRIGHTNESSUP,	POWERBOOK_FLAG_FKEY },
> +	{ KEY_F3,	KEY_MUTE,		POWERBOOK_FLAG_FKEY },
> +	{ KEY_F4,	KEY_VOLUMEDOWN,		POWERBOOK_FLAG_FKEY },
> +	{ KEY_F5,	KEY_VOLUMEUP,		POWERBOOK_FLAG_FKEY },
> +	{ KEY_F6,	KEY_NUMLOCK,		POWERBOOK_FLAG_FKEY },
> +	{ KEY_F7,	KEY_SWITCHVIDEOMODE,	POWERBOOK_FLAG_FKEY },
> +	{ KEY_F8,	KEY_KBDILLUMTOGGLE,	POWERBOOK_FLAG_FKEY },
> +	{ KEY_F9,	KEY_KBDILLUMDOWN,	POWERBOOK_FLAG_FKEY },
> +	{ KEY_F10,	KEY_KBDILLUMUP,		POWERBOOK_FLAG_FKEY },
> +	{ KEY_UP,	KEY_PAGEUP },
> +	{ KEY_DOWN,	KEY_PAGEDOWN },
> +	{ KEY_LEFT,	KEY_HOME },
> +	{ KEY_RIGHT,	KEY_END },
> +	{ }
> +};
> +
> +static struct hidinput_key_translation powerbook_numlock_keys[] = {
> +	{ KEY_J,	KEY_KP1 },
> +	{ KEY_K,	KEY_KP2 },
> +	{ KEY_L,	KEY_KP3 },
> +	{ KEY_U,	KEY_KP4 },
> +	{ KEY_I,	KEY_KP5 },
> +	{ KEY_O,	KEY_KP6 },
> +	{ KEY_7,	KEY_KP7 },
> +	{ KEY_8,	KEY_KP8 },
> +	{ KEY_9,	KEY_KP9 },
> +	{ KEY_M,	KEY_KP0 },
> +	{ KEY_DOT,	KEY_KPDOT },
> +	{ KEY_SLASH,	KEY_KPPLUS },
> +	{ KEY_SEMICOLON, KEY_KPMINUS },
> +	{ KEY_P,	KEY_KPASTERISK },
> +	{ KEY_MINUS,	KEY_KPEQUAL },
> +	{ KEY_0,	KEY_KPSLASH },
> +	{ KEY_F6,	KEY_NUMLOCK },
> +	{ KEY_KPENTER,	KEY_KPENTER },
> +	{ KEY_BACKSPACE, KEY_BACKSPACE },
> +	{ }
> +};
> +
> +static int usbhid_pb_fnmode = 1;
> +module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644);
> +MODULE_PARM_DESC(pb_fnmode,
> +	"Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)");
> +
> +static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from)
> +{
> +	struct hidinput_key_translation *trans;
> +
> +	/* Look for the translation */
> +	for (trans = table; trans->from; trans++)
> +		if (trans->from == from)
> +			return trans;
> +
> +	return NULL;
> +}
> +
> +static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
> +			     struct hid_usage *usage, __s32 value)
> +{
> +	struct hidinput_key_translation *trans;
> +
> +	if (usage->code == KEY_FN) {
> +		if (value) hid->quirks |=  HID_QUIRK_POWERBOOK_FN_ON;
> +		else       hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON;
> +
> +		input_event(input, usage->type, usage->code, value);
> +
> +		return 1;
> +	}
> +
> +	if (usbhid_pb_fnmode) {
> +		int do_translate;
> +
> +		trans = find_translation(powerbook_fn_keys, usage->code);
> +		if (trans) {
> +			if (test_bit(usage->code, hid->pb_pressed_fn))
> +				do_translate = 1;
> +			else if (trans->flags & POWERBOOK_FLAG_FKEY)
> +				do_translate =
> +					(usbhid_pb_fnmode == 2 &&  (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) ||
> +					(usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON));
> +			else
> +				do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON);
> +
> +			if (do_translate) {
> +				if (value)
> +					set_bit(usage->code, hid->pb_pressed_fn);
> +				else
> +					clear_bit(usage->code, hid->pb_pressed_fn);
> +
> +				input_event(input, usage->type, trans->to, value);
> +
> +				return 1;
> +			}
> +		}
> +
> +		if (test_bit(usage->code, hid->pb_pressed_numlock) ||
> +		    test_bit(LED_NUML, input->led)) {
> +			trans = find_translation(powerbook_numlock_keys, usage->code);
> +
> +			if (trans) {
> +				if (value)
> +					set_bit(usage->code, hid->pb_pressed_numlock);
> +				else
> +					clear_bit(usage->code, hid->pb_pressed_numlock);
> +
> +				input_event(input, usage->type, trans->to, value);
> +			}
> +
> +			return 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static void hidinput_pb_setup(struct input_dev *input)
> +{
> +	struct hidinput_key_translation *trans;
> +
> +	set_bit(KEY_NUMLOCK, input->keybit);
> +
> +	/* Enable all needed keys */
> +	for (trans = powerbook_fn_keys; trans->from; trans++)
> +		set_bit(trans->to, input->keybit);
> +
> +	for (trans = powerbook_numlock_keys; trans->from; trans++)
> +		set_bit(trans->to, input->keybit);
> +}
> +#else
> +static inline int hidinput_pb_event(struct hid_device *hid, struct input_dev *input,
> +				    struct hid_usage *usage, __s32 value)
> +{
> +	return 0;
> +}
> +
> +static inline void hidinput_pb_setup(struct input_dev *input)
> +{
> +}
> +#endif
> +
>  static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
>  				     struct hid_usage *usage)
>  {
> @@ -336,7 +490,12 @@ static void hidinput_configure_usage(str
>  
>  			set_bit(EV_REP, input->evbit);
>  			switch(usage->hid & HID_USAGE) {
> -				case 0x003: map_key_clear(KEY_FN);		break;
> +				case 0x003:
> +					/* The fn key on Apple PowerBooks */
> +					map_key_clear(KEY_FN);
> +					hidinput_pb_setup(input);
> +					break;
> +
>  				default:    goto ignore;
>  			}
>  			break;
> @@ -493,6 +652,9 @@ void hidinput_hid_event(struct hid_devic
>  		return;
>  	}
>  
> +	if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value))
> +		return;
> +
>  	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
>  		int hat_dir = usage->hat_dir;
>  		if (!hat_dir)
> @@ -535,7 +697,7 @@ void hidinput_hid_event(struct hid_devic
>  		return;
>  	}
>  
> -	if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
> +	if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */
>  		return;
>  
>  	input_event(input, usage->type, usage->code, value);
> Index: work/drivers/usb/input/Kconfig
> ===================================================================
> --- work.orig/drivers/usb/input/Kconfig
> +++ work/drivers/usb/input/Kconfig
> @@ -37,6 +37,16 @@ config USB_HIDINPUT
>  
>  	  If unsure, say Y.
>  
> +config USB_HIDINPUT_POWERBOOK
> +	bool "Enable support for iBook/PowerBook special keys"
> +	default n
> +	depends on USB_HIDINPUT
> +	help
> +	  Say Y here if you want support for the special keys (Fn, Numlock) on
> +	  Apple iBooks and PowerBooks.
> +
> +	  If unsure, say N.
> +
>  config HID_FF
>  	bool "Force feedback support (EXPERIMENTAL)"
>  	depends on USB_HIDINPUT && EXPERIMENTAL
> 
> 

-- 
Vojtech Pavlik
SuSE Labs, SuSE CR

^ permalink raw reply	[flat|nested] 46+ messages in thread

* Re: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
  2006-01-14  4:58                     ` Dmitry Torokhov
  2006-01-14 10:41                       ` Vojtech Pavlik
@ 2006-01-14 10:57                       ` Michael Hanselmann
  1 sibling, 0 replies; 46+ messages in thread
From: Michael Hanselmann @ 2006-01-14 10:57 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Vojtech Pavlik, Benjamin Herrenschmidt, linux-kernel,
	linux-input, linuxppc-dev, linux-kernel

Hello Dmitry

On Fri, Jan 13, 2006 at 11:58:33PM -0500, Dmitry Torokhov wrote:
> One little thing - I think that we should report FN key even if PowerBook
> support is disabled. Can I solicit input on the patch below (I also rearranged
> teh code slightly)? 

Yeah, all looks fine for me.

Greets,
Michael

^ permalink raw reply	[flat|nested] 46+ messages in thread

end of thread, other threads:[~2006-01-14 10:57 UTC | newest]

Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-12-25 21:20 [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks Michael Hanselmann
2005-12-25 21:57 ` Benjamin Herrenschmidt
2005-12-26  4:04 ` Dmitry Torokhov
2005-12-26  5:46   ` Benjamin Herrenschmidt
2006-01-11 21:07     ` Dmitry Torokhov
2006-01-11 21:20       ` Michael Hanselmann
2006-01-11 21:34         ` Benjamin Herrenschmidt
2006-01-11 21:38           ` Michael Hanselmann
2006-01-11 21:41             ` Benjamin Herrenschmidt
2006-01-11 21:43               ` Michael Hanselmann
2006-01-11 21:47                 ` Vojtech Pavlik
2006-01-11 21:50                   ` Michael Hanselmann
2006-01-11 21:54                 ` Benjamin Herrenschmidt
2006-01-11 21:30       ` Benjamin Herrenschmidt
2006-01-11 21:45         ` Vojtech Pavlik
2006-01-11 21:46         ` Michael Hanselmann
2006-01-11 23:26       ` Michael Hanselmann
2006-01-11 23:41         ` Benjamin Herrenschmidt
2006-01-12  0:08           ` Michael Hanselmann
2006-01-13  4:12             ` Dmitry Torokhov
2006-01-13  6:53               ` Michael Hanselmann
2006-01-13  7:47                 ` Vojtech Pavlik
2006-01-13 22:02                   ` Michael Hanselmann
2006-01-14  4:58                     ` Dmitry Torokhov
2006-01-14 10:41                       ` Vojtech Pavlik
2006-01-14 10:57                       ` Michael Hanselmann
2006-01-13 21:55               ` Benjamin Herrenschmidt
2006-01-13 21:57                 ` Benjamin Herrenschmidt
2006-01-13 22:05                 ` Dmitry Torokhov
2006-01-13 22:08                   ` Dmitry Torokhov
2006-01-13 22:14                   ` Benjamin Herrenschmidt
2006-01-13 22:25                     ` Dmitry Torokhov
2006-01-12  9:07           ` Vojtech Pavlik
2006-01-12 23:39             ` Michael Hanselmann
2006-01-13  1:53               ` Benjamin Herrenschmidt
2005-12-31 23:51   ` Michael Hanselmann
2006-01-01  1:33     ` Michael Hanselmann
2006-01-01  2:56     ` Benjamin Herrenschmidt
2006-01-01  3:03       ` Michael Hanselmann
2006-01-01  6:09         ` Benjamin Herrenschmidt
2006-01-02 22:46       ` Michael Hanselmann
2006-01-03  2:29         ` Ben Collins
2006-01-03 19:14           ` Michael Hanselmann
2006-01-03 19:18             ` Ben Collins
2006-01-03 19:25               ` Michael Hanselmann
2006-01-02 12:06     ` Stelian Pop

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).