All of lore.kernel.org
 help / color / mirror / Atom feed
From: Michael Hanselmann <linux-kernel@hansmi.ch>
To: linux-kernel@vger.kernel.org
Cc: linux-input@atrey.karlin.mff.cuni.cz, linuxppc-dev@ozlabs.org,
	linux-kernel@killerfox.forkbomb.ch
Subject: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
Date: Sun, 25 Dec 2005 22:20:41 +0100	[thread overview]
Message-ID: <20051225212041.GA6094@hansmi.ch> (raw)

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

WARNING: multiple messages have this Message-ID (diff)
From: Michael Hanselmann <linux-kernel@hansmi.ch>
To: linux-kernel@vger.kernel.org
Cc: linux-input@atrey.karlin.mff.cuni.cz, linuxppc-dev@ozlabs.org,
	benh@kernel.crashing.org, linux-kernel@killerfox.forkbomb.ch
Subject: [PATCH/RFC?] usb/input: Add support for fn key on Apple PowerBooks
Date: Sun, 25 Dec 2005 22:20:41 +0100	[thread overview]
Message-ID: <20051225212041.GA6094@hansmi.ch> (raw)

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

             reply	other threads:[~2005-12-25 21:20 UTC|newest]

Thread overview: 92+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-12-25 21:20 Michael Hanselmann [this message]
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-25 21:57   ` Benjamin Herrenschmidt
2005-12-26  4:04 ` Dmitry Torokhov
2005-12-26  4:04   ` Dmitry Torokhov
2005-12-26  5:46   ` Benjamin Herrenschmidt
2005-12-26  5:46     ` Benjamin Herrenschmidt
2006-01-11 21:07     ` Dmitry Torokhov
2006-01-11 21:07       ` Dmitry Torokhov
2006-01-11 21:20       ` Michael Hanselmann
2006-01-11 21:20         ` Michael Hanselmann
2006-01-11 21:34         ` Benjamin Herrenschmidt
2006-01-11 21:34           ` Benjamin Herrenschmidt
2006-01-11 21:38           ` Michael Hanselmann
2006-01-11 21:38             ` Michael Hanselmann
2006-01-11 21:41             ` Benjamin Herrenschmidt
2006-01-11 21:41               ` Benjamin Herrenschmidt
2006-01-11 21:43               ` Michael Hanselmann
2006-01-11 21:43                 ` Michael Hanselmann
2006-01-11 21:47                 ` Vojtech Pavlik
2006-01-11 21:47                   ` Vojtech Pavlik
2006-01-11 21:50                   ` Michael Hanselmann
2006-01-11 21:50                     ` Michael Hanselmann
2006-01-11 21:54                 ` Benjamin Herrenschmidt
2006-01-11 21:54                   ` Benjamin Herrenschmidt
2006-01-11 21:30       ` Benjamin Herrenschmidt
2006-01-11 21:30         ` Benjamin Herrenschmidt
2006-01-11 21:45         ` Vojtech Pavlik
2006-01-11 21:45           ` Vojtech Pavlik
2006-01-11 21:46         ` Michael Hanselmann
2006-01-11 21:46           ` Michael Hanselmann
2006-01-11 23:26       ` Michael Hanselmann
2006-01-11 23:26         ` Michael Hanselmann
2006-01-11 23:41         ` Benjamin Herrenschmidt
2006-01-11 23:41           ` Benjamin Herrenschmidt
2006-01-12  0:08           ` Michael Hanselmann
2006-01-12  0:08             ` Michael Hanselmann
2006-01-13  4:12             ` Dmitry Torokhov
2006-01-13  4:12               ` Dmitry Torokhov
2006-01-13  6:53               ` Michael Hanselmann
2006-01-13  6:53                 ` Michael Hanselmann
2006-01-13  7:47                 ` Vojtech Pavlik
2006-01-13  7:47                   ` Vojtech Pavlik
2006-01-13 22:02                   ` Michael Hanselmann
2006-01-13 22:02                     ` Michael Hanselmann
2006-01-14  4:58                     ` Dmitry Torokhov
2006-01-14  4:58                       ` Dmitry Torokhov
2006-01-14 10:41                       ` Vojtech Pavlik
2006-01-14 10:41                         ` Vojtech Pavlik
2006-01-14 10:57                       ` Michael Hanselmann
2006-01-14 10:57                         ` Michael Hanselmann
2006-01-13 21:55               ` Benjamin Herrenschmidt
2006-01-13 21:55                 ` Benjamin Herrenschmidt
2006-01-13 21:57                 ` Benjamin Herrenschmidt
2006-01-13 21:57                   ` Benjamin Herrenschmidt
2006-01-13 22:05                 ` Dmitry Torokhov
2006-01-13 22:05                   ` Dmitry Torokhov
2006-01-13 22:08                   ` Dmitry Torokhov
2006-01-13 22:08                     ` Dmitry Torokhov
2006-01-13 22:14                   ` Benjamin Herrenschmidt
2006-01-13 22:14                     ` Benjamin Herrenschmidt
2006-01-13 22:25                     ` Dmitry Torokhov
2006-01-13 22:25                       ` Dmitry Torokhov
2006-01-12  9:07           ` Vojtech Pavlik
2006-01-12  9:07             ` Vojtech Pavlik
2006-01-12 23:39             ` Michael Hanselmann
2006-01-12 23:39               ` Michael Hanselmann
2006-01-13  1:53               ` Benjamin Herrenschmidt
2006-01-13  1:53                 ` Benjamin Herrenschmidt
2005-12-31 23:51   ` Michael Hanselmann
2005-12-31 23:51     ` Michael Hanselmann
2006-01-01  1:33     ` Michael Hanselmann
2006-01-01  1:33       ` Michael Hanselmann
2006-01-01  2:56     ` Benjamin Herrenschmidt
2006-01-01  2:56       ` Benjamin Herrenschmidt
2006-01-01  3:03       ` Michael Hanselmann
2006-01-01  3:03         ` Michael Hanselmann
2006-01-01  6:09         ` Benjamin Herrenschmidt
2006-01-01  6:09           ` Benjamin Herrenschmidt
2006-01-02 22:46       ` Michael Hanselmann
2006-01-02 22:46         ` Michael Hanselmann
2006-01-03  2:29         ` Ben Collins
2006-01-03  2:29           ` Ben Collins
2006-01-03 19:14           ` Michael Hanselmann
2006-01-03 19:14             ` Michael Hanselmann
2006-01-03 19:18             ` Ben Collins
2006-01-03 19:18               ` Ben Collins
2006-01-03 19:25               ` Michael Hanselmann
2006-01-03 19:25                 ` Michael Hanselmann
2006-01-02 12:06     ` Stelian Pop
2006-01-02 12:06       ` Stelian Pop

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20051225212041.GA6094@hansmi.ch \
    --to=linux-kernel@hansmi.ch \
    --cc=linux-input@atrey.karlin.mff.cuni.cz \
    --cc=linux-kernel@killerfox.forkbomb.ch \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linuxppc-dev@ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.