aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLv Zheng <lv.zheng@intel.com>2016-08-17 04:22:58 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-08-30 19:06:20 -0400
commitdfa46c50f65b6ca10cea389327a6f1f1749bc633 (patch)
tree961f5e140d2c56eda0c59becbfcedc3d408680d8
parent3eab887a55424fc2c27553b7bfe32330df83f7b8 (diff)
ACPI / button: Fix an issue in button.lid_init_state=ignore mode
On most platforms, _LID returning value, lid open/close events are all reliable, but there are exceptions. Some AML tables report wrong initial lid state [1], and some of them never report lid open state [2]. The usage model on such buggy platforms is: 1. The initial lid state returned from _LID is not reliable; 2. The lid open event is not reliable; 3. The lid close event is always reliable, used by the platform firmware to trigger OSPM power saving operations. This usage model is not compliant to the Linux SW_LID model as the Linux userspace is very strict to the reliability of the open events. In order not to trigger issues on such buggy platforms, the ACPI button driver currently implements a lid_init_state=open quirk to send additional "open" event after resuming. However, this is still not sufficient because: 1. Some special usage models (e.x., the dark resume scenario) cannot be supported by this mode. 2. If a "close" event is not used to trigger "suspend", then the subsequent "close" events cannot be seen by the userspace. So we need to stop sending the additional "open" event and switch the driver to lid_init_state=ignore mode and make sure the platform triggered events can be reliably delivered to the userspace. The userspace programs then can be changed to not to be strict to the "open" events on such buggy platforms. Why will the subsequent "close" events be lost? This is because the input layer automatically filters redundant events for switch events. Thus given that the buggy AML tables do not guarantee paired "open"/"close" events, the ACPI button driver currently is not able to guarantee that the platform triggered reliable events can be always be seen by the userspace via SW_LID. This patch adds a mechanism to insert lid events as a compensation for the platform triggered ones to form a complete event switches in order to make sure that the platform triggered events can always be reliably delivered to the userspace. This essentially guarantees that the platform triggered reliable "close" events will always be relibly delivered to the userspace. However this mechanism is not suitable for lid_init_state=open/method as it should not send the complement switch event for the unreliable initial lid state notification. 2 unreliable events can trigger unexpected behavior. Thus this patch only implements this mechanism for lid_init_state=ignore. Known issues: 1. Possible alternative approach This approach is based on the fact that Linux requires a switch event type for LID events. Another approach is to use key event type to implement ACPI lid events. With SW event type, since ACPI button driver inserts wrong lid events, there could be a potential issue that an "open" event issued from some AML update methods could result in a wrong "close" event to be delivered to the userspace. While using KEY event type, there is no such problem. However there may not be such a kind of real case, and if there is such a case, it is worked around in this patch as the complement switch event is only generated for "close" event in order to deliver the reliable "close" event to the userspace. Link: https://bugzilla.kernel.org/show_bug.cgi?id=89211 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=106151 # [1] Link: https://bugzilla.kernel.org/show_bug.cgi?id=106941 # [2] Signed-off-by: Lv Zheng <lv.zheng@intel.com> Suggested-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/button.c85
1 files changed, 82 insertions, 3 deletions
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 31abb0bdd4f2..e19f530f1083 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -19,6 +19,8 @@
19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 19 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20 */ 20 */
21 21
22#define pr_fmt(fmt) "ACPI : button: " fmt
23
22#include <linux/kernel.h> 24#include <linux/kernel.h>
23#include <linux/module.h> 25#include <linux/module.h>
24#include <linux/init.h> 26#include <linux/init.h>
@@ -104,6 +106,8 @@ struct acpi_button {
104 struct input_dev *input; 106 struct input_dev *input;
105 char phys[32]; /* for input device */ 107 char phys[32]; /* for input device */
106 unsigned long pushed; 108 unsigned long pushed;
109 int last_state;
110 ktime_t last_time;
107 bool suspended; 111 bool suspended;
108}; 112};
109 113
@@ -111,6 +115,10 @@ static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
111static struct acpi_device *lid_device; 115static struct acpi_device *lid_device;
112static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; 116static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
113 117
118static unsigned long lid_report_interval __read_mostly = 500;
119module_param(lid_report_interval, ulong, 0644);
120MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
121
114/* -------------------------------------------------------------------------- 122/* --------------------------------------------------------------------------
115 FS Interface (/proc) 123 FS Interface (/proc)
116 -------------------------------------------------------------------------- */ 124 -------------------------------------------------------------------------- */
@@ -134,10 +142,79 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state)
134{ 142{
135 struct acpi_button *button = acpi_driver_data(device); 143 struct acpi_button *button = acpi_driver_data(device);
136 int ret; 144 int ret;
145 ktime_t next_report;
146 bool do_update;
147
148 /*
149 * In lid_init_state=ignore mode, if user opens/closes lid
150 * frequently with "open" missing, and "last_time" is also updated
151 * frequently, "close" cannot be delivered to the userspace.
152 * So "last_time" is only updated after a timeout or an actual
153 * switch.
154 */
155 if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
156 button->last_state != !!state)
157 do_update = true;
158 else
159 do_update = false;
160
161 next_report = ktime_add(button->last_time,
162 ms_to_ktime(lid_report_interval));
163 if (button->last_state == !!state &&
164 ktime_after(ktime_get(), next_report)) {
165 /* Complain the buggy firmware */
166 pr_warn_once("The lid device is not compliant to SW_LID.\n");
137 167
138 /* input layer checks if event is redundant */ 168 /*
139 input_report_switch(button->input, SW_LID, !state); 169 * Send the unreliable complement switch event:
140 input_sync(button->input); 170 *
171 * On most platforms, the lid device is reliable. However
172 * there are exceptions:
173 * 1. Platforms returning initial lid state as "close" by
174 * default after booting/resuming:
175 * https://bugzilla.kernel.org/show_bug.cgi?id=89211
176 * https://bugzilla.kernel.org/show_bug.cgi?id=106151
177 * 2. Platforms never reporting "open" events:
178 * https://bugzilla.kernel.org/show_bug.cgi?id=106941
179 * On these buggy platforms, the usage model of the ACPI
180 * lid device actually is:
181 * 1. The initial returning value of _LID may not be
182 * reliable.
183 * 2. The open event may not be reliable.
184 * 3. The close event is reliable.
185 *
186 * But SW_LID is typed as input switch event, the input
187 * layer checks if the event is redundant. Hence if the
188 * state is not switched, the userspace cannot see this
189 * platform triggered reliable event. By inserting a
190 * complement switch event, it then is guaranteed that the
191 * platform triggered reliable one can always be seen by
192 * the userspace.
193 */
194 if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
195 do_update = true;
196 /*
197 * Do generate complement switch event for "close"
198 * as "close" is reliable and wrong "open" won't
199 * trigger unexpected behaviors.
200 * Do not generate complement switch event for
201 * "open" as "open" is not reliable and wrong
202 * "close" will trigger unexpected behaviors.
203 */
204 if (!state) {
205 input_report_switch(button->input,
206 SW_LID, state);
207 input_sync(button->input);
208 }
209 }
210 }
211 /* Send the platform triggered reliable event */
212 if (do_update) {
213 input_report_switch(button->input, SW_LID, !state);
214 input_sync(button->input);
215 button->last_state = !!state;
216 button->last_time = ktime_get();
217 }
141 218
142 if (state) 219 if (state)
143 pm_wakeup_event(&device->dev, 0); 220 pm_wakeup_event(&device->dev, 0);
@@ -411,6 +488,8 @@ static int acpi_button_add(struct acpi_device *device)
411 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID); 488 strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
412 sprintf(class, "%s/%s", 489 sprintf(class, "%s/%s",
413 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); 490 ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
491 button->last_state = !!acpi_lid_evaluate_state(device);
492 button->last_time = ktime_get();
414 } else { 493 } else {
415 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid); 494 printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
416 error = -ENODEV; 495 error = -ENODEV;