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

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

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Vojtech Pavlik <vojtech@suse.cz>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by Michael Hanselmann and committed by Dmitry Torokhov eab9edd2 1e27ffd4

+201 -14
+10
drivers/usb/input/Kconfig
··· 37 38 If unsure, say Y. 39 40 config HID_FF 41 bool "Force feedback support (EXPERIMENTAL)" 42 depends on USB_HIDINPUT && EXPERIMENTAL
··· 37 38 If unsure, say Y. 39 40 + config USB_HIDINPUT_POWERBOOK 41 + bool "Enable support for iBook/PowerBook special keys" 42 + default n 43 + depends on USB_HIDINPUT 44 + help 45 + Say Y here if you want support for the special keys (Fn, Numlock) on 46 + Apple iBooks and PowerBooks. 47 + 48 + If unsure, say N. 49 + 50 config HID_FF 51 bool "Force feedback support (EXPERIMENTAL)" 52 depends on USB_HIDINPUT && EXPERIMENTAL
+8
drivers/usb/input/hid-core.c
··· 1585 1586 { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, 1587 1588 { 0, 0 } 1589 }; 1590
··· 1585 1586 { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, 1587 1588 + { USB_VENDOR_ID_APPLE, 0x020E, HID_QUIRK_POWERBOOK_HAS_FN }, 1589 + { USB_VENDOR_ID_APPLE, 0x020F, HID_QUIRK_POWERBOOK_HAS_FN }, 1590 + { USB_VENDOR_ID_APPLE, 0x0214, HID_QUIRK_POWERBOOK_HAS_FN }, 1591 + { USB_VENDOR_ID_APPLE, 0x0215, HID_QUIRK_POWERBOOK_HAS_FN }, 1592 + { USB_VENDOR_ID_APPLE, 0x0216, HID_QUIRK_POWERBOOK_HAS_FN }, 1593 + { USB_VENDOR_ID_APPLE, 0x030A, HID_QUIRK_POWERBOOK_HAS_FN }, 1594 + { USB_VENDOR_ID_APPLE, 0x030B, HID_QUIRK_POWERBOOK_HAS_FN }, 1595 + 1596 { 0, 0 } 1597 }; 1598
+164 -2
drivers/usb/input/hid-input.c
··· 73 #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) 74 #define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0) 75 76 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, 77 struct hid_usage *usage) 78 { ··· 490 491 set_bit(EV_REP, input->evbit); 492 switch(usage->hid & HID_USAGE) { 493 - case 0x003: map_key_clear(KEY_FN); break; 494 default: goto ignore; 495 } 496 break; ··· 652 return; 653 } 654 655 if (usage->hat_min < usage->hat_max || usage->hat_dir) { 656 int hat_dir = usage->hat_dir; 657 if (!hat_dir) ··· 697 return; 698 } 699 700 - if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ 701 return; 702 703 input_event(input, usage->type, usage->code, value);
··· 73 #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) 74 #define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0) 75 76 + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK 77 + 78 + struct hidinput_key_translation { 79 + u16 from; 80 + u16 to; 81 + u8 flags; 82 + }; 83 + 84 + #define POWERBOOK_FLAG_FKEY 0x01 85 + 86 + static struct hidinput_key_translation powerbook_fn_keys[] = { 87 + { KEY_BACKSPACE, KEY_DELETE }, 88 + { KEY_F1, KEY_BRIGHTNESSDOWN, POWERBOOK_FLAG_FKEY }, 89 + { KEY_F2, KEY_BRIGHTNESSUP, POWERBOOK_FLAG_FKEY }, 90 + { KEY_F3, KEY_MUTE, POWERBOOK_FLAG_FKEY }, 91 + { KEY_F4, KEY_VOLUMEDOWN, POWERBOOK_FLAG_FKEY }, 92 + { KEY_F5, KEY_VOLUMEUP, POWERBOOK_FLAG_FKEY }, 93 + { KEY_F6, KEY_NUMLOCK, POWERBOOK_FLAG_FKEY }, 94 + { KEY_F7, KEY_SWITCHVIDEOMODE, POWERBOOK_FLAG_FKEY }, 95 + { KEY_F8, KEY_KBDILLUMTOGGLE, POWERBOOK_FLAG_FKEY }, 96 + { KEY_F9, KEY_KBDILLUMDOWN, POWERBOOK_FLAG_FKEY }, 97 + { KEY_F10, KEY_KBDILLUMUP, POWERBOOK_FLAG_FKEY }, 98 + { KEY_UP, KEY_PAGEUP }, 99 + { KEY_DOWN, KEY_PAGEDOWN }, 100 + { KEY_LEFT, KEY_HOME }, 101 + { KEY_RIGHT, KEY_END }, 102 + { } 103 + }; 104 + 105 + static struct hidinput_key_translation powerbook_numlock_keys[] = { 106 + { KEY_J, KEY_KP1 }, 107 + { KEY_K, KEY_KP2 }, 108 + { KEY_L, KEY_KP3 }, 109 + { KEY_U, KEY_KP4 }, 110 + { KEY_I, KEY_KP5 }, 111 + { KEY_O, KEY_KP6 }, 112 + { KEY_7, KEY_KP7 }, 113 + { KEY_8, KEY_KP8 }, 114 + { KEY_9, KEY_KP9 }, 115 + { KEY_M, KEY_KP0 }, 116 + { KEY_DOT, KEY_KPDOT }, 117 + { KEY_SLASH, KEY_KPPLUS }, 118 + { KEY_SEMICOLON, KEY_KPMINUS }, 119 + { KEY_P, KEY_KPASTERISK }, 120 + { KEY_MINUS, KEY_KPEQUAL }, 121 + { KEY_0, KEY_KPSLASH }, 122 + { KEY_F6, KEY_NUMLOCK }, 123 + { KEY_KPENTER, KEY_KPENTER }, 124 + { KEY_BACKSPACE, KEY_BACKSPACE }, 125 + { } 126 + }; 127 + 128 + static int usbhid_pb_fnmode = 1; 129 + module_param_named(pb_fnmode, usbhid_pb_fnmode, int, 0644); 130 + MODULE_PARM_DESC(pb_fnmode, 131 + "Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = fkeysfirst)"); 132 + 133 + static struct hidinput_key_translation *find_translation(struct hidinput_key_translation *table, u16 from) 134 + { 135 + struct hidinput_key_translation *trans; 136 + 137 + /* Look for the translation */ 138 + for (trans = table; trans->from; trans++) 139 + if (trans->from == from) 140 + return trans; 141 + 142 + return NULL; 143 + } 144 + 145 + static int hidinput_pb_event(struct hid_device *hid, struct input_dev *input, 146 + struct hid_usage *usage, __s32 value) 147 + { 148 + struct hidinput_key_translation *trans; 149 + 150 + if (usage->code == KEY_FN) { 151 + if (value) hid->quirks |= HID_QUIRK_POWERBOOK_FN_ON; 152 + else hid->quirks &= ~HID_QUIRK_POWERBOOK_FN_ON; 153 + 154 + input_event(input, usage->type, usage->code, value); 155 + 156 + return 1; 157 + } 158 + 159 + if (usbhid_pb_fnmode) { 160 + int do_translate; 161 + 162 + trans = find_translation(powerbook_fn_keys, usage->code); 163 + if (trans) { 164 + if (test_bit(usage->code, hid->pb_pressed_fn)) 165 + do_translate = 1; 166 + else if (trans->flags & POWERBOOK_FLAG_FKEY) 167 + do_translate = 168 + (usbhid_pb_fnmode == 2 && (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)) || 169 + (usbhid_pb_fnmode == 1 && !(hid->quirks & HID_QUIRK_POWERBOOK_FN_ON)); 170 + else 171 + do_translate = (hid->quirks & HID_QUIRK_POWERBOOK_FN_ON); 172 + 173 + if (do_translate) { 174 + if (value) 175 + set_bit(usage->code, hid->pb_pressed_fn); 176 + else 177 + clear_bit(usage->code, hid->pb_pressed_fn); 178 + 179 + input_event(input, usage->type, trans->to, value); 180 + 181 + return 1; 182 + } 183 + } 184 + 185 + if (test_bit(usage->code, hid->pb_pressed_numlock) || 186 + test_bit(LED_NUML, input->led)) { 187 + trans = find_translation(powerbook_numlock_keys, usage->code); 188 + 189 + if (trans) { 190 + if (value) 191 + set_bit(usage->code, hid->pb_pressed_numlock); 192 + else 193 + clear_bit(usage->code, hid->pb_pressed_numlock); 194 + 195 + input_event(input, usage->type, trans->to, value); 196 + } 197 + 198 + return 1; 199 + } 200 + } 201 + 202 + return 0; 203 + } 204 + 205 + static void hidinput_pb_setup(struct input_dev *input) 206 + { 207 + struct hidinput_key_translation *trans; 208 + 209 + set_bit(KEY_NUMLOCK, input->keybit); 210 + 211 + /* Enable all needed keys */ 212 + for (trans = powerbook_fn_keys; trans->from; trans++) 213 + set_bit(trans->to, input->keybit); 214 + 215 + for (trans = powerbook_numlock_keys; trans->from; trans++) 216 + set_bit(trans->to, input->keybit); 217 + } 218 + #else 219 + static inline int hidinput_pb_event(struct hid_device *hid, struct input_dev *input, 220 + struct hid_usage *usage, __s32 value) 221 + { 222 + return 0; 223 + } 224 + 225 + static inline void hidinput_pb_setup(struct input_dev *input) 226 + { 227 + } 228 + #endif 229 + 230 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, 231 struct hid_usage *usage) 232 { ··· 336 337 set_bit(EV_REP, input->evbit); 338 switch(usage->hid & HID_USAGE) { 339 + case 0x003: 340 + /* The fn key on Apple PowerBooks */ 341 + map_key_clear(KEY_FN); 342 + hidinput_pb_setup(input); 343 + break; 344 + 345 default: goto ignore; 346 } 347 break; ··· 493 return; 494 } 495 496 + if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value)) 497 + return; 498 + 499 if (usage->hat_min < usage->hat_max || usage->hat_dir) { 500 int hat_dir = usage->hat_dir; 501 if (!hat_dir) ··· 535 return; 536 } 537 538 + if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ 539 return; 540 541 input_event(input, usage->type, usage->code, value);
+19 -12
drivers/usb/input/hid.h
··· 235 * HID device quirks. 236 */ 237 238 - #define HID_QUIRK_INVERT 0x001 239 - #define HID_QUIRK_NOTOUCH 0x002 240 - #define HID_QUIRK_IGNORE 0x004 241 - #define HID_QUIRK_NOGET 0x008 242 - #define HID_QUIRK_HIDDEV 0x010 243 - #define HID_QUIRK_BADPAD 0x020 244 - #define HID_QUIRK_MULTI_INPUT 0x040 245 - #define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x080 246 - #define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x100 247 - #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x200 248 - #define HID_QUIRK_2WHEEL_POWERMOUSE 0x400 249 - #define HID_QUIRK_CYMOTION 0x800 250 251 /* 252 * This is the global environment of the parser. This information is ··· 434 void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ 435 int (*ff_event)(struct hid_device *hid, struct input_dev *input, 436 unsigned int type, unsigned int code, int value); 437 }; 438 439 #define HID_GLOBAL_STACK_SIZE 4
··· 235 * HID device quirks. 236 */ 237 238 + #define HID_QUIRK_INVERT 0x00000001 239 + #define HID_QUIRK_NOTOUCH 0x00000002 240 + #define HID_QUIRK_IGNORE 0x00000004 241 + #define HID_QUIRK_NOGET 0x00000008 242 + #define HID_QUIRK_HIDDEV 0x00000010 243 + #define HID_QUIRK_BADPAD 0x00000020 244 + #define HID_QUIRK_MULTI_INPUT 0x00000040 245 + #define HID_QUIRK_2WHEEL_MOUSE_HACK_7 0x00000080 246 + #define HID_QUIRK_2WHEEL_MOUSE_HACK_5 0x00000100 247 + #define HID_QUIRK_2WHEEL_MOUSE_HACK_ON 0x00000200 248 + #define HID_QUIRK_2WHEEL_POWERMOUSE 0x00000400 249 + #define HID_QUIRK_CYMOTION 0x00000800 250 + #define HID_QUIRK_POWERBOOK_HAS_FN 0x00001000 251 + #define HID_QUIRK_POWERBOOK_FN_ON 0x00002000 252 253 /* 254 * This is the global environment of the parser. This information is ··· 432 void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ 433 int (*ff_event)(struct hid_device *hid, struct input_dev *input, 434 unsigned int type, unsigned int code, int value); 435 + 436 + #ifdef CONFIG_USB_HIDINPUT_POWERBOOK 437 + unsigned long pb_pressed_fn[NBITS(KEY_MAX)]; 438 + unsigned long pb_pressed_numlock[NBITS(KEY_MAX)]; 439 + #endif 440 }; 441 442 #define HID_GLOBAL_STACK_SIZE 4