diff options
author | Oleksij Rempel <linux@rempel-privat.de> | 2016-09-12 11:48:17 -0400 |
---|---|---|
committer | Darren Hart <dvhart@linux.intel.com> | 2016-09-23 20:06:20 -0400 |
commit | b5643539b82559b858b8efe3fc8343f66cf9a0b5 (patch) | |
tree | 75ab060185392155db9458d856a7ac158bac21e6 /drivers/platform | |
parent | 28e476d7438eedd3ecc7b5ebe6f2a3dfd21ca10e (diff) |
platform/x86: asus-wmi: Filter buggy scan codes on ASUS Q500A
Some revisions of the ASUS Q500A series have a keyboard related
issue which is reproducible only after Windows with installed ASUS
tools is started.
In this case the Linux side will have a blocked keyboard or
report incorrect or incomplete hotkey events.
To make Linux work properly again, a complete power down
(unplug power supply and remove battery) is needed.
Linux/atkbd after a clean start will get the following code on VOLUME_UP
key: {0xe0, 0x30, 0xe0, 0xb0}. After Windows, the same key will generate
this codes: {0xe1, 0x23, 0xe0, 0x30, 0xe0, 0xb0}. As result atkdb will
be confused by buggy codes.
This patch is filtering this buggy code out.
https://bugzilla.kernel.org/show_bug.cgi?id=119391
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Cc: Alex Henrie <alexhenrie24@gmail.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Corentin Chary <corentin.chary@gmail.com>
Cc: acpi4asus-user@lists.sourceforge.net
Cc: platform-driver-x86@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
[dvhart: Add return after pr_warn to avoid false confirmation of filter]
Signed-off-by: Darren Hart <dvhart@linux.intel.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 45 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.h | 4 |
2 files changed, 49 insertions, 0 deletions
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index f4a60e75cd8a..26e4cbc34db8 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/input/sparse-keymap.h> | 27 | #include <linux/input/sparse-keymap.h> |
28 | #include <linux/fb.h> | 28 | #include <linux/fb.h> |
29 | #include <linux/dmi.h> | 29 | #include <linux/dmi.h> |
30 | #include <linux/i8042.h> | ||
30 | 31 | ||
31 | #include "asus-wmi.h" | 32 | #include "asus-wmi.h" |
32 | 33 | ||
@@ -55,10 +56,34 @@ MODULE_PARM_DESC(wapf, "WAPF value"); | |||
55 | 56 | ||
56 | static struct quirk_entry *quirks; | 57 | static struct quirk_entry *quirks; |
57 | 58 | ||
59 | static bool asus_q500a_i8042_filter(unsigned char data, unsigned char str, | ||
60 | struct serio *port) | ||
61 | { | ||
62 | static bool extended; | ||
63 | bool ret = false; | ||
64 | |||
65 | if (str & I8042_STR_AUXDATA) | ||
66 | return false; | ||
67 | |||
68 | if (unlikely(data == 0xe1)) { | ||
69 | extended = true; | ||
70 | ret = true; | ||
71 | } else if (unlikely(extended)) { | ||
72 | extended = false; | ||
73 | ret = true; | ||
74 | } | ||
75 | |||
76 | return ret; | ||
77 | } | ||
78 | |||
58 | static struct quirk_entry quirk_asus_unknown = { | 79 | static struct quirk_entry quirk_asus_unknown = { |
59 | .wapf = 0, | 80 | .wapf = 0, |
60 | }; | 81 | }; |
61 | 82 | ||
83 | static struct quirk_entry quirk_asus_q500a = { | ||
84 | .i8042_filter = asus_q500a_i8042_filter, | ||
85 | }; | ||
86 | |||
62 | /* | 87 | /* |
63 | * For those machines that need software to control bt/wifi status | 88 | * For those machines that need software to control bt/wifi status |
64 | * and can't adjust brightness through ACPI interface | 89 | * and can't adjust brightness through ACPI interface |
@@ -100,6 +125,15 @@ static int dmi_matched(const struct dmi_system_id *dmi) | |||
100 | static const struct dmi_system_id asus_quirks[] = { | 125 | static const struct dmi_system_id asus_quirks[] = { |
101 | { | 126 | { |
102 | .callback = dmi_matched, | 127 | .callback = dmi_matched, |
128 | .ident = "ASUSTeK COMPUTER INC. Q500A", | ||
129 | .matches = { | ||
130 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), | ||
131 | DMI_MATCH(DMI_PRODUCT_NAME, "Q500A"), | ||
132 | }, | ||
133 | .driver_data = &quirk_asus_q500a, | ||
134 | }, | ||
135 | { | ||
136 | .callback = dmi_matched, | ||
103 | .ident = "ASUSTeK COMPUTER INC. U32U", | 137 | .ident = "ASUSTeK COMPUTER INC. U32U", |
104 | .matches = { | 138 | .matches = { |
105 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), | 139 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), |
@@ -369,6 +403,8 @@ static const struct dmi_system_id asus_quirks[] = { | |||
369 | 403 | ||
370 | static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) | 404 | static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) |
371 | { | 405 | { |
406 | int ret; | ||
407 | |||
372 | quirks = &quirk_asus_unknown; | 408 | quirks = &quirk_asus_unknown; |
373 | dmi_check_system(asus_quirks); | 409 | dmi_check_system(asus_quirks); |
374 | 410 | ||
@@ -380,6 +416,15 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) | |||
380 | quirks->wapf = wapf; | 416 | quirks->wapf = wapf; |
381 | else | 417 | else |
382 | wapf = quirks->wapf; | 418 | wapf = quirks->wapf; |
419 | |||
420 | if (quirks->i8042_filter) { | ||
421 | ret = i8042_install_filter(quirks->i8042_filter); | ||
422 | if (ret) { | ||
423 | pr_warn("Unable to install key filter\n"); | ||
424 | return; | ||
425 | } | ||
426 | pr_info("Using i8042 filter function for receiving events\n"); | ||
427 | } | ||
383 | } | 428 | } |
384 | 429 | ||
385 | static const struct key_entry asus_nb_wmi_keymap[] = { | 430 | static const struct key_entry asus_nb_wmi_keymap[] = { |
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 5e220411056d..0e19014e9f54 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h | |||
@@ -28,6 +28,7 @@ | |||
28 | #define _ASUS_WMI_H_ | 28 | #define _ASUS_WMI_H_ |
29 | 29 | ||
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/i8042.h> | ||
31 | 32 | ||
32 | #define ASUS_WMI_KEY_IGNORE (-1) | 33 | #define ASUS_WMI_KEY_IGNORE (-1) |
33 | #define ASUS_WMI_BRN_DOWN 0x20 | 34 | #define ASUS_WMI_BRN_DOWN 0x20 |
@@ -52,6 +53,9 @@ struct quirk_entry { | |||
52 | * and let the ACPI interrupt to send out the key event. | 53 | * and let the ACPI interrupt to send out the key event. |
53 | */ | 54 | */ |
54 | int no_display_toggle; | 55 | int no_display_toggle; |
56 | |||
57 | bool (*i8042_filter)(unsigned char data, unsigned char str, | ||
58 | struct serio *serio); | ||
55 | }; | 59 | }; |
56 | 60 | ||
57 | struct asus_wmi_driver { | 61 | struct asus_wmi_driver { |