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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
---
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 ([email protected])
+ *
+ * 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
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 <[email protected]>
> Acked-by: Rene Nussbaumer <[email protected]>
> Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
This makes the keyboard useable on latest generations of Apple
PowerBooks, so please apply asap :)
Ben.
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
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.
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
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
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.
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
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.
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 <[email protected]>
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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 <[email protected]>
Developer
Ubuntu Linux
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
> +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 <[email protected]>
Developer
Ubuntu Linux
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
On 12/26/05, Benjamin Herrenschmidt <[email protected]> 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
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
> 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.
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.
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
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.
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
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
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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
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
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.
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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.
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
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
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 <[email protected]>
> Acked-by: Rene Nussbaumer <[email protected]>
> Acked-by: Johannes Berg <[email protected]>
> Acked-by: Benjamin Herrenschmidt <[email protected]>
>
> ---
> @@ -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
> 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.
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.
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
---
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
On 1/13/06, Benjamin Herrenschmidt <[email protected]> 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
On 1/13/06, Dmitry Torokhov <[email protected]> wrote:
> On 1/13/06, Benjamin Herrenschmidt <[email protected]> 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
> 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.
On 1/13/06, Benjamin Herrenschmidt <[email protected]> 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
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 <[email protected]>
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 <[email protected]>
Acked-by: Rene Nussbaumer <[email protected]>
Acked-by: Johannes Berg <[email protected]>
Acked-by: Benjamin Herrenschmidt <[email protected]>
Signed-off-by: Dmitry Torokhov <[email protected]>
---
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
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 <[email protected]>
>
> 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 <[email protected]>
> Acked-by: Rene Nussbaumer <[email protected]>
> Acked-by: Johannes Berg <[email protected]>
> Acked-by: Benjamin Herrenschmidt <[email protected]>
>
> Signed-off-by: Dmitry Torokhov <[email protected]>
> ---
>
> 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
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