diff options
-rw-r--r-- | drivers/acpi/button.c | 85 |
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); | |||
111 | static struct acpi_device *lid_device; | 115 | static struct acpi_device *lid_device; |
112 | static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; | 116 | static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
113 | 117 | ||
118 | static unsigned long lid_report_interval __read_mostly = 500; | ||
119 | module_param(lid_report_interval, ulong, 0644); | ||
120 | MODULE_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; |