| From b7213b5088e609ca3f4ce3ce05c5d2b56c7e0a23 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Tue, 3 Sep 2019 16:46:32 +0200 |
| Subject: HID: apple: Fix stuck function keys when using FN |
| |
| From: Joao Moreno <mail@joaomoreno.com> |
| |
| [ Upstream commit aec256d0ecd561036f188dbc8fa7924c47a9edfd ] |
| |
| This fixes an issue in which key down events for function keys would be |
| repeatedly emitted even after the user has raised the physical key. For |
| example, the driver fails to emit the F5 key up event when going through |
| the following steps: |
| - fnmode=1: hold FN, hold F5, release FN, release F5 |
| - fnmode=2: hold F5, hold FN, release F5, release FN |
| |
| The repeated F5 key down events can be easily verified using xev. |
| |
| Signed-off-by: Joao Moreno <mail@joaomoreno.com> |
| Co-developed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> |
| Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/hid/hid-apple.c | 49 +++++++++++++++++++++++------------------ |
| 1 file changed, 28 insertions(+), 21 deletions(-) |
| |
| diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c |
| index 884d82f9190e2..8af87dc05f2a5 100644 |
| --- a/drivers/hid/hid-apple.c |
| +++ b/drivers/hid/hid-apple.c |
| @@ -55,7 +55,6 @@ MODULE_PARM_DESC(swap_opt_cmd, "Swap the Option (\"Alt\") and Command (\"Flag\") |
| struct apple_sc { |
| unsigned long quirks; |
| unsigned int fn_on; |
| - DECLARE_BITMAP(pressed_fn, KEY_CNT); |
| DECLARE_BITMAP(pressed_numlock, KEY_CNT); |
| }; |
| |
| @@ -182,6 +181,8 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, |
| { |
| struct apple_sc *asc = hid_get_drvdata(hid); |
| const struct apple_key_translation *trans, *table; |
| + bool do_translate; |
| + u16 code = 0; |
| |
| if (usage->code == KEY_FN) { |
| asc->fn_on = !!value; |
| @@ -190,8 +191,6 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, |
| } |
| |
| if (fnmode) { |
| - int do_translate; |
| - |
| if (hid->product >= USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI && |
| hid->product <= USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS) |
| table = macbookair_fn_keys; |
| @@ -203,25 +202,33 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, |
| trans = apple_find_translation (table, usage->code); |
| |
| if (trans) { |
| - if (test_bit(usage->code, asc->pressed_fn)) |
| - do_translate = 1; |
| - else if (trans->flags & APPLE_FLAG_FKEY) |
| - do_translate = (fnmode == 2 && asc->fn_on) || |
| - (fnmode == 1 && !asc->fn_on); |
| - else |
| - do_translate = asc->fn_on; |
| - |
| - if (do_translate) { |
| - if (value) |
| - set_bit(usage->code, asc->pressed_fn); |
| - else |
| - clear_bit(usage->code, asc->pressed_fn); |
| - |
| - input_event(input, usage->type, trans->to, |
| - value); |
| - |
| - return 1; |
| + if (test_bit(trans->from, input->key)) |
| + code = trans->from; |
| + else if (test_bit(trans->to, input->key)) |
| + code = trans->to; |
| + |
| + if (!code) { |
| + if (trans->flags & APPLE_FLAG_FKEY) { |
| + switch (fnmode) { |
| + case 1: |
| + do_translate = !asc->fn_on; |
| + break; |
| + case 2: |
| + do_translate = asc->fn_on; |
| + break; |
| + default: |
| + /* should never happen */ |
| + do_translate = false; |
| + } |
| + } else { |
| + do_translate = asc->fn_on; |
| + } |
| + |
| + code = do_translate ? trans->to : trans->from; |
| } |
| + |
| + input_event(input, usage->type, code, value); |
| + return 1; |
| } |
| |
| if (asc->quirks & APPLE_NUMLOCK_EMULATION && |
| -- |
| 2.20.1 |
| |