summaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorOleksij Rempel <linux@rempel-privat.de>2016-09-12 11:48:17 -0400
committerDarren Hart <dvhart@linux.intel.com>2016-09-23 20:06:20 -0400
commitb5643539b82559b858b8efe3fc8343f66cf9a0b5 (patch)
tree75ab060185392155db9458d856a7ac158bac21e6 /drivers/platform
parent28e476d7438eedd3ecc7b5ebe6f2a3dfd21ca10e (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.c45
-rw-r--r--drivers/platform/x86/asus-wmi.h4
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
56static struct quirk_entry *quirks; 57static struct quirk_entry *quirks;
57 58
59static 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
58static struct quirk_entry quirk_asus_unknown = { 79static struct quirk_entry quirk_asus_unknown = {
59 .wapf = 0, 80 .wapf = 0,
60}; 81};
61 82
83static 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)
100static const struct dmi_system_id asus_quirks[] = { 125static 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
370static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) 404static 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
385static const struct key_entry asus_nb_wmi_keymap[] = { 430static 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
57struct asus_wmi_driver { 61struct asus_wmi_driver {