diff options
Diffstat (limited to 'drivers/usb/input/hid-input.c')
-rw-r--r-- | drivers/usb/input/hid-input.c | 166 |
1 files changed, 164 insertions, 2 deletions
diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c index 36a08c04c46..cb0d80f4925 100644 --- a/drivers/usb/input/hid-input.c +++ b/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(struct hid_input *hidinput, struct hid_fiel 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_device *hid, struct hid_field *field, struct 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_device *hid, struct hid_field *field, struct 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); |