aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/button.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/button.c')
-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;