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 37 38 38 If unsure, say Y. 39 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 + 40 50 config HID_FF 41 51 bool "Force feedback support (EXPERIMENTAL)" 42 52 depends on USB_HIDINPUT && EXPERIMENTAL
+8
drivers/usb/input/hid-core.c
··· 1585 1585 1586 1586 { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, 1587 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 + 1588 1596 { 0, 0 } 1589 1597 }; 1590 1598
+164 -2
drivers/usb/input/hid-input.c
··· 73 73 #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) 74 74 #define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0) 75 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 + 76 230 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field, 77 231 struct hid_usage *usage) 78 232 { ··· 490 336 491 337 set_bit(EV_REP, input->evbit); 492 338 switch(usage->hid & HID_USAGE) { 493 - case 0x003: map_key_clear(KEY_FN); break; 339 + case 0x003: 340 + /* The fn key on Apple PowerBooks */ 341 + map_key_clear(KEY_FN); 342 + hidinput_pb_setup(input); 343 + break; 344 + 494 345 default: goto ignore; 495 346 } 496 347 break; ··· 652 493 return; 653 494 } 654 495 496 + if ((hid->quirks & HID_QUIRK_POWERBOOK_HAS_FN) && hidinput_pb_event(hid, input, usage, value)) 497 + return; 498 + 655 499 if (usage->hat_min < usage->hat_max || usage->hat_dir) { 656 500 int hat_dir = usage->hat_dir; 657 501 if (!hat_dir) ··· 697 535 return; 698 536 } 699 537 700 - if((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ 538 + if ((usage->type == EV_KEY) && (usage->code == 0)) /* Key 0 is "unassigned", not KEY_UNKNOWN */ 701 539 return; 702 540 703 541 input_event(input, usage->type, usage->code, value);
+19 -12
drivers/usb/input/hid.h
··· 235 235 * HID device quirks. 236 236 */ 237 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 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 250 252 251 253 /* 252 254 * This is the global environment of the parser. This information is ··· 434 432 void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */ 435 433 int (*ff_event)(struct hid_device *hid, struct input_dev *input, 436 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 437 440 }; 438 441 439 442 #define HID_GLOBAL_STACK_SIZE 4