2019-05-27 14:55:06 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2009-04-08 23:39:38 +08:00
|
|
|
* button.c - ACPI Button Driver
|
2005-04-17 06:20:36 +08:00
|
|
|
*
|
|
|
|
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
|
|
|
|
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
|
|
|
|
*/
|
|
|
|
|
2017-05-21 02:38:13 +08:00
|
|
|
#define pr_fmt(fmt) "ACPI: button: " fmt
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
|
2018-07-07 23:25:01 +08:00
|
|
|
#include <linux/compiler.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/init.h>
|
2005-08-04 05:55:21 +08:00
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/proc_fs.h>
|
|
|
|
#include <linux/seq_file.h>
|
2006-11-09 13:40:13 +08:00
|
|
|
#include <linux/input.h>
|
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h
percpu.h is included by sched.h and module.h and thus ends up being
included when building most .c files. percpu.h includes slab.h which
in turn includes gfp.h making everything defined by the two files
universally available and complicating inclusion dependencies.
percpu.h -> slab.h dependency is about to be removed. Prepare for
this change by updating users of gfp and slab facilities include those
headers directly instead of assuming availability. As this conversion
needs to touch large number of source files, the following script is
used as the basis of conversion.
http://userweb.kernel.org/~tj/misc/slabh-sweep.py
The script does the followings.
* Scan files for gfp and slab usages and update includes such that
only the necessary includes are there. ie. if only gfp is used,
gfp.h, if slab is used, slab.h.
* When the script inserts a new include, it looks at the include
blocks and try to put the new include such that its order conforms
to its surrounding. It's put in the include block which contains
core kernel includes, in the same order that the rest are ordered -
alphabetical, Christmas tree, rev-Xmas-tree or at the end if there
doesn't seem to be any matching order.
* If the script can't find a place to put a new include (mostly
because the file doesn't have fitting include block), it prints out
an error message indicating which .h file needs to be added to the
file.
The conversion was done in the following steps.
1. The initial automatic conversion of all .c files updated slightly
over 4000 files, deleting around 700 includes and adding ~480 gfp.h
and ~3000 slab.h inclusions. The script emitted errors for ~400
files.
2. Each error was manually checked. Some didn't need the inclusion,
some needed manual addition while adding it to implementation .h or
embedding .c file was more appropriate for others. This step added
inclusions to around 150 files.
3. The script was run again and the output was compared to the edits
from #2 to make sure no file was left behind.
4. Several build tests were done and a couple of problems were fixed.
e.g. lib/decompress_*.c used malloc/free() wrappers around slab
APIs requiring slab.h to be added manually.
5. The script was run on all .h files but without automatically
editing them as sprinkling gfp.h and slab.h inclusions around .h
files could easily lead to inclusion dependency hell. Most gfp.h
inclusion directives were ignored as stuff from gfp.h was usually
wildly available and often used in preprocessor macros. Each
slab.h inclusion directive was examined and added manually as
necessary.
6. percpu.h was updated not to include slab.h.
7. Build test were done on the following configurations and failures
were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my
distributed build env didn't work with gcov compiles) and a few
more options had to be turned off depending on archs to make things
build (like ipr on powerpc/64 which failed due to missing writeq).
* x86 and x86_64 UP and SMP allmodconfig and a custom test config.
* powerpc and powerpc64 SMP allmodconfig
* sparc and sparc64 SMP allmodconfig
* ia64 SMP allmodconfig
* s390 SMP allmodconfig
* alpha SMP allmodconfig
* um on x86_64 SMP allmodconfig
8. percpu.h modifications were reverted so that it could be applied as
a separate patch and serve as bisection point.
Given the fact that I had only a couple of failures from tests on step
6, I'm fairly confident about the coverage of this conversion patch.
If there is a breakage, it's likely to be something in one of the arch
headers which should be easily discoverable easily on most builds of
the specific arch.
Signed-off-by: Tejun Heo <tj@kernel.org>
Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 16:04:11 +08:00
|
|
|
#include <linux/slab.h>
|
2013-12-03 08:49:16 +08:00
|
|
|
#include <linux/acpi.h>
|
2017-11-22 23:06:12 +08:00
|
|
|
#include <linux/dmi.h>
|
2013-03-11 17:17:04 +08:00
|
|
|
#include <acpi/button.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2009-07-29 04:45:54 +08:00
|
|
|
#define PREFIX "ACPI: "
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define ACPI_BUTTON_CLASS "button"
|
2005-08-04 05:55:21 +08:00
|
|
|
#define ACPI_BUTTON_FILE_INFO "info"
|
|
|
|
#define ACPI_BUTTON_FILE_STATE "state"
|
|
|
|
#define ACPI_BUTTON_TYPE_UNKNOWN 0x00
|
2005-04-17 06:20:36 +08:00
|
|
|
#define ACPI_BUTTON_NOTIFY_STATUS 0x80
|
|
|
|
|
|
|
|
#define ACPI_BUTTON_SUBCLASS_POWER "power"
|
2005-08-05 12:44:28 +08:00
|
|
|
#define ACPI_BUTTON_HID_POWER "PNP0C0C"
|
2009-04-08 23:40:04 +08:00
|
|
|
#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button"
|
2005-04-17 06:20:36 +08:00
|
|
|
#define ACPI_BUTTON_TYPE_POWER 0x01
|
|
|
|
|
|
|
|
#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep"
|
|
|
|
#define ACPI_BUTTON_HID_SLEEP "PNP0C0E"
|
2009-04-08 23:40:04 +08:00
|
|
|
#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button"
|
2005-04-17 06:20:36 +08:00
|
|
|
#define ACPI_BUTTON_TYPE_SLEEP 0x03
|
|
|
|
|
|
|
|
#define ACPI_BUTTON_SUBCLASS_LID "lid"
|
|
|
|
#define ACPI_BUTTON_HID_LID "PNP0C0D"
|
|
|
|
#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch"
|
|
|
|
#define ACPI_BUTTON_TYPE_LID 0x05
|
|
|
|
|
2016-06-01 18:10:48 +08:00
|
|
|
#define ACPI_BUTTON_LID_INIT_IGNORE 0x00
|
|
|
|
#define ACPI_BUTTON_LID_INIT_OPEN 0x01
|
2017-05-09 15:02:22 +08:00
|
|
|
#define ACPI_BUTTON_LID_INIT_METHOD 0x02
|
2016-06-01 18:10:48 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define _COMPONENT ACPI_BUTTON_COMPONENT
|
2007-02-13 11:42:12 +08:00
|
|
|
ACPI_MODULE_NAME("button");
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
MODULE_AUTHOR("Paul Diefenbaugh");
|
2007-02-13 12:50:02 +08:00
|
|
|
MODULE_DESCRIPTION("ACPI Button Driver");
|
2005-04-17 06:20:36 +08:00
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2007-07-23 20:44:41 +08:00
|
|
|
static const struct acpi_device_id button_device_ids[] = {
|
|
|
|
{ACPI_BUTTON_HID_LID, 0},
|
|
|
|
{ACPI_BUTTON_HID_SLEEP, 0},
|
|
|
|
{ACPI_BUTTON_HID_SLEEPF, 0},
|
|
|
|
{ACPI_BUTTON_HID_POWER, 0},
|
|
|
|
{ACPI_BUTTON_HID_POWERF, 0},
|
|
|
|
{"", 0},
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(acpi, button_device_ids);
|
|
|
|
|
2017-11-22 23:06:12 +08:00
|
|
|
/*
|
|
|
|
* Some devices which don't even have a lid in anyway have a broken _LID
|
|
|
|
* method (e.g. pointing to a floating gpio pin) causing spurious LID events.
|
|
|
|
*/
|
|
|
|
static const struct dmi_system_id lid_blacklst[] = {
|
|
|
|
{
|
|
|
|
/* GP-electronic T701 */
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
|
|
|
|
DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
|
|
|
|
},
|
|
|
|
},
|
2024-06-11 20:08:33 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Medion Akoya E2215T, notification of the LID device only
|
|
|
|
* happens on close, not on open and _LID always returns closed.
|
|
|
|
*/
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "E2215T MD60198"),
|
|
|
|
},
|
|
|
|
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Razer Blade Stealth 13 late 2019, notification of the LID device
|
|
|
|
* only happens on close, not on open and _LID always returns closed.
|
|
|
|
*/
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Razer"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "Razer Blade Stealth 13 Late 2019"),
|
|
|
|
},
|
|
|
|
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
|
|
|
|
},
|
2017-11-22 23:06:12 +08:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2005-08-05 12:44:28 +08:00
|
|
|
static int acpi_button_add(struct acpi_device *device);
|
2013-01-24 07:24:48 +08:00
|
|
|
static int acpi_button_remove(struct acpi_device *device);
|
2009-03-31 01:48:18 +08:00
|
|
|
static void acpi_button_notify(struct acpi_device *device, u32 event);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-08-10 05:00:02 +08:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2014-07-23 06:59:04 +08:00
|
|
|
static int acpi_button_suspend(struct device *dev);
|
2012-06-28 05:26:51 +08:00
|
|
|
static int acpi_button_resume(struct device *dev);
|
2014-02-13 11:19:07 +08:00
|
|
|
#else
|
2014-07-23 06:59:04 +08:00
|
|
|
#define acpi_button_suspend NULL
|
2014-02-13 11:19:07 +08:00
|
|
|
#define acpi_button_resume NULL
|
2012-08-10 05:00:02 +08:00
|
|
|
#endif
|
2014-07-23 06:59:04 +08:00
|
|
|
static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume);
|
2012-06-28 05:26:51 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct acpi_driver acpi_button_driver = {
|
2007-02-13 12:33:40 +08:00
|
|
|
.name = "button",
|
2005-08-05 12:44:28 +08:00
|
|
|
.class = ACPI_BUTTON_CLASS,
|
2007-07-23 20:44:41 +08:00
|
|
|
.ids = button_device_ids,
|
2005-08-05 12:44:28 +08:00
|
|
|
.ops = {
|
|
|
|
.add = acpi_button_add,
|
|
|
|
.remove = acpi_button_remove,
|
2009-03-31 01:48:18 +08:00
|
|
|
.notify = acpi_button_notify,
|
2006-11-09 13:40:13 +08:00
|
|
|
},
|
2012-06-28 05:26:51 +08:00
|
|
|
.drv.pm = &acpi_button_pm,
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct acpi_button {
|
2006-11-09 13:40:13 +08:00
|
|
|
unsigned int type;
|
|
|
|
struct input_dev *input;
|
|
|
|
char phys[32]; /* for input device */
|
2005-08-05 12:44:28 +08:00
|
|
|
unsigned long pushed;
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
int last_state;
|
|
|
|
ktime_t last_time;
|
2014-07-23 06:59:04 +08:00
|
|
|
bool suspended;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2009-09-11 06:28:02 +08:00
|
|
|
static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier);
|
|
|
|
static struct acpi_device *lid_device;
|
Revert "ACPI / button: Change default behavior to lid_init_state=open"
Revert commit 77e9a4aa9de1 (ACPI / button: Change default behavior to
lid_init_state=open) which changed the kernel's behavior on laptops
that boot with closed lids and expect the lid switch state to be
reported accurately by the kernel.
If you boot or resume your laptop with the lid closed on a docking
station while using an external monitor connected to it, both internal
and external displays will light on, while only the external should.
There is a design choice in gdm to only provide the greeter on the
internal display when lit on, so users only see a gray area on the
external monitor. Also, the cursor will not show up as it's by
default on the internal display too.
To "fix" that, users have to open the laptop once and close it once
again to sync the state of the switch with the hardware state.
Even if the "method" operation mode implementation can be buggy on
some platforms, the "open" choice is worse. It breaks docking
stations basically and there is no way to have a user-space hwdb to
fix that.
On the contrary, it's rather easy in user-space to have a hwdb
with the problematic platforms. Then, libinput (1.7.0+) can fix
the state of the lid switch for us: you need to set the udev
property LIBINPUT_ATTR_LID_SWITCH_RELIABILITY to 'write_open'.
When libinput detects internal keyboard events, it will overwrite the
state of the switch to open, making it reliable again. Given that
logind only checks the lid switch value after a timeout, we can
assume the user will use the internal keyboard before this timeout
expires.
For example, such a hwdb entry is:
libinput:name:*Lid Switch*:dmi:*svnMicrosoftCorporation:pnSurface3:*
LIBINPUT_ATTR_LID_SWITCH_RELIABILITY=write_open
Link: https://bugzilla.gnome.org/show_bug.cgi?id=782380
Cc: 4.11+ <stable@vger.kernel.org> # 4.11+
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2017-05-11 00:12:40 +08:00
|
|
|
static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
|
2009-09-11 06:28:02 +08:00
|
|
|
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
static unsigned long lid_report_interval __read_mostly = 500;
|
|
|
|
module_param(lid_report_interval, ulong, 0644);
|
|
|
|
MODULE_PARM_DESC(lid_report_interval, "Interval (ms) between lid key events");
|
|
|
|
|
2005-08-04 05:55:21 +08:00
|
|
|
/* --------------------------------------------------------------------------
|
|
|
|
FS Interface (/proc)
|
|
|
|
-------------------------------------------------------------------------- */
|
|
|
|
|
2005-08-05 12:44:28 +08:00
|
|
|
static struct proc_dir_entry *acpi_button_dir;
|
2011-03-23 10:21:40 +08:00
|
|
|
static struct proc_dir_entry *acpi_lid_dir;
|
2005-08-05 12:44:28 +08:00
|
|
|
|
2016-06-01 18:10:42 +08:00
|
|
|
static int acpi_lid_evaluate_state(struct acpi_device *device)
|
|
|
|
{
|
|
|
|
unsigned long long lid_state;
|
|
|
|
acpi_status status;
|
|
|
|
|
|
|
|
status = acpi_evaluate_integer(device->handle, "_LID", NULL, &lid_state);
|
|
|
|
if (ACPI_FAILURE(status))
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
return lid_state ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int acpi_lid_notify_state(struct acpi_device *device, int state)
|
|
|
|
{
|
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
|
|
|
int ret;
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
ktime_t next_report;
|
|
|
|
bool do_update;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In lid_init_state=ignore mode, if user opens/closes lid
|
|
|
|
* frequently with "open" missing, and "last_time" is also updated
|
|
|
|
* frequently, "close" cannot be delivered to the userspace.
|
|
|
|
* So "last_time" is only updated after a timeout or an actual
|
|
|
|
* switch.
|
|
|
|
*/
|
|
|
|
if (lid_init_state != ACPI_BUTTON_LID_INIT_IGNORE ||
|
|
|
|
button->last_state != !!state)
|
|
|
|
do_update = true;
|
|
|
|
else
|
|
|
|
do_update = false;
|
|
|
|
|
|
|
|
next_report = ktime_add(button->last_time,
|
|
|
|
ms_to_ktime(lid_report_interval));
|
|
|
|
if (button->last_state == !!state &&
|
|
|
|
ktime_after(ktime_get(), next_report)) {
|
|
|
|
/* Complain the buggy firmware */
|
|
|
|
pr_warn_once("The lid device is not compliant to SW_LID.\n");
|
2016-06-01 18:10:42 +08:00
|
|
|
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
/*
|
|
|
|
* Send the unreliable complement switch event:
|
|
|
|
*
|
|
|
|
* On most platforms, the lid device is reliable. However
|
|
|
|
* there are exceptions:
|
|
|
|
* 1. Platforms returning initial lid state as "close" by
|
|
|
|
* default after booting/resuming:
|
|
|
|
* https://bugzilla.kernel.org/show_bug.cgi?id=89211
|
|
|
|
* https://bugzilla.kernel.org/show_bug.cgi?id=106151
|
|
|
|
* 2. Platforms never reporting "open" events:
|
|
|
|
* https://bugzilla.kernel.org/show_bug.cgi?id=106941
|
|
|
|
* On these buggy platforms, the usage model of the ACPI
|
|
|
|
* lid device actually is:
|
|
|
|
* 1. The initial returning value of _LID may not be
|
|
|
|
* reliable.
|
|
|
|
* 2. The open event may not be reliable.
|
|
|
|
* 3. The close event is reliable.
|
|
|
|
*
|
|
|
|
* But SW_LID is typed as input switch event, the input
|
|
|
|
* layer checks if the event is redundant. Hence if the
|
|
|
|
* state is not switched, the userspace cannot see this
|
|
|
|
* platform triggered reliable event. By inserting a
|
|
|
|
* complement switch event, it then is guaranteed that the
|
|
|
|
* platform triggered reliable one can always be seen by
|
|
|
|
* the userspace.
|
|
|
|
*/
|
|
|
|
if (lid_init_state == ACPI_BUTTON_LID_INIT_IGNORE) {
|
|
|
|
do_update = true;
|
|
|
|
/*
|
|
|
|
* Do generate complement switch event for "close"
|
|
|
|
* as "close" is reliable and wrong "open" won't
|
|
|
|
* trigger unexpected behaviors.
|
|
|
|
* Do not generate complement switch event for
|
|
|
|
* "open" as "open" is not reliable and wrong
|
|
|
|
* "close" will trigger unexpected behaviors.
|
|
|
|
*/
|
|
|
|
if (!state) {
|
|
|
|
input_report_switch(button->input,
|
|
|
|
SW_LID, state);
|
|
|
|
input_sync(button->input);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Send the platform triggered reliable event */
|
|
|
|
if (do_update) {
|
2017-11-22 23:06:11 +08:00
|
|
|
acpi_handle_debug(device->handle, "ACPI LID %s\n",
|
|
|
|
state ? "open" : "closed");
|
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>
2016-08-17 16:22:58 +08:00
|
|
|
input_report_switch(button->input, SW_LID, !state);
|
|
|
|
input_sync(button->input);
|
|
|
|
button->last_state = !!state;
|
|
|
|
button->last_time = ktime_get();
|
|
|
|
}
|
2016-06-01 18:10:42 +08:00
|
|
|
|
|
|
|
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device);
|
|
|
|
if (ret == NOTIFY_DONE)
|
|
|
|
ret = blocking_notifier_call_chain(&acpi_lid_notifier, state,
|
|
|
|
device);
|
|
|
|
if (ret == NOTIFY_DONE || ret == NOTIFY_OK) {
|
|
|
|
/*
|
|
|
|
* It is also regarded as success if the notifier_chain
|
|
|
|
* returns NOTIFY_OK or NOTIFY_DONE.
|
|
|
|
*/
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-07 23:25:01 +08:00
|
|
|
static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq,
|
|
|
|
void *offset)
|
2005-08-04 05:55:21 +08:00
|
|
|
{
|
2009-04-08 23:39:59 +08:00
|
|
|
struct acpi_device *device = seq->private;
|
2016-06-01 18:10:42 +08:00
|
|
|
int state;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2016-06-01 18:10:42 +08:00
|
|
|
state = acpi_lid_evaluate_state(device);
|
2006-11-09 13:40:13 +08:00
|
|
|
seq_printf(seq, "state: %s\n",
|
2016-06-01 18:10:42 +08:00
|
|
|
state < 0 ? "unsupported" : (state ? "open" : "closed"));
|
2006-06-27 12:41:40 +08:00
|
|
|
return 0;
|
2005-08-04 05:55:21 +08:00
|
|
|
}
|
|
|
|
|
2005-08-05 12:44:28 +08:00
|
|
|
static int acpi_button_add_fs(struct acpi_device *device)
|
2005-08-04 05:55:21 +08:00
|
|
|
{
|
2009-04-08 23:39:49 +08:00
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
2005-08-05 12:44:28 +08:00
|
|
|
struct proc_dir_entry *entry = NULL;
|
2011-03-23 10:21:40 +08:00
|
|
|
int ret = 0;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
/* procfs I/F for ACPI lid device only */
|
|
|
|
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (acpi_button_dir || acpi_lid_dir) {
|
|
|
|
printk(KERN_ERR PREFIX "More than one Lid device found!\n");
|
|
|
|
return -EEXIST;
|
2005-08-04 05:55:21 +08:00
|
|
|
}
|
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
/* create /proc/acpi/button */
|
|
|
|
acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
|
|
|
|
if (!acpi_button_dir)
|
2006-06-27 12:41:40 +08:00
|
|
|
return -ENODEV;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
/* create /proc/acpi/button/lid */
|
|
|
|
acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
|
|
|
if (!acpi_lid_dir) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto remove_button_dir;
|
|
|
|
}
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
/* create /proc/acpi/button/lid/LID/ */
|
|
|
|
acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), acpi_lid_dir);
|
|
|
|
if (!acpi_device_dir(device)) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto remove_lid_dir;
|
|
|
|
}
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
/* create /proc/acpi/button/lid/LID/state */
|
2018-05-15 21:57:23 +08:00
|
|
|
entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO,
|
|
|
|
acpi_device_dir(device), acpi_button_state_seq_show,
|
|
|
|
device);
|
2011-03-23 10:21:40 +08:00
|
|
|
if (!entry) {
|
|
|
|
ret = -ENODEV;
|
|
|
|
goto remove_dev_dir;
|
2005-08-04 05:55:21 +08:00
|
|
|
}
|
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
done:
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
remove_dev_dir:
|
|
|
|
remove_proc_entry(acpi_device_bid(device),
|
|
|
|
acpi_lid_dir);
|
|
|
|
acpi_device_dir(device) = NULL;
|
|
|
|
remove_lid_dir:
|
|
|
|
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
2016-07-29 23:08:41 +08:00
|
|
|
acpi_lid_dir = NULL;
|
2011-03-23 10:21:40 +08:00
|
|
|
remove_button_dir:
|
|
|
|
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
2016-07-29 23:08:41 +08:00
|
|
|
acpi_button_dir = NULL;
|
2011-03-23 10:21:40 +08:00
|
|
|
goto done;
|
2005-08-04 05:55:21 +08:00
|
|
|
}
|
|
|
|
|
2005-08-05 12:44:28 +08:00
|
|
|
static int acpi_button_remove_fs(struct acpi_device *device)
|
2005-08-04 05:55:21 +08:00
|
|
|
{
|
2006-11-09 13:40:13 +08:00
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
if (button->type != ACPI_BUTTON_TYPE_LID)
|
|
|
|
return 0;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2011-03-23 10:21:40 +08:00
|
|
|
remove_proc_entry(ACPI_BUTTON_FILE_STATE,
|
|
|
|
acpi_device_dir(device));
|
|
|
|
remove_proc_entry(acpi_device_bid(device),
|
|
|
|
acpi_lid_dir);
|
|
|
|
acpi_device_dir(device) = NULL;
|
|
|
|
remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
|
2016-07-29 23:08:41 +08:00
|
|
|
acpi_lid_dir = NULL;
|
2011-03-23 10:21:40 +08:00
|
|
|
remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
|
2016-07-29 23:08:41 +08:00
|
|
|
acpi_button_dir = NULL;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2006-06-27 12:41:40 +08:00
|
|
|
return 0;
|
2005-08-04 05:55:21 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* --------------------------------------------------------------------------
|
|
|
|
Driver Interface
|
|
|
|
-------------------------------------------------------------------------- */
|
2009-09-11 06:28:02 +08:00
|
|
|
int acpi_lid_notifier_register(struct notifier_block *nb)
|
|
|
|
{
|
|
|
|
return blocking_notifier_chain_register(&acpi_lid_notifier, nb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_lid_notifier_register);
|
|
|
|
|
|
|
|
int acpi_lid_notifier_unregister(struct notifier_block *nb)
|
|
|
|
{
|
|
|
|
return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_lid_notifier_unregister);
|
|
|
|
|
|
|
|
int acpi_lid_open(void)
|
|
|
|
{
|
2009-10-08 05:39:46 +08:00
|
|
|
if (!lid_device)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2016-06-01 18:10:42 +08:00
|
|
|
return acpi_lid_evaluate_state(lid_device);
|
2009-09-11 06:28:02 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(acpi_lid_open);
|
|
|
|
|
2018-06-28 01:55:02 +08:00
|
|
|
static int acpi_lid_update_state(struct acpi_device *device,
|
|
|
|
bool signal_wakeup)
|
2007-10-22 18:18:18 +08:00
|
|
|
{
|
2016-06-01 18:10:42 +08:00
|
|
|
int state;
|
2009-04-08 23:39:38 +08:00
|
|
|
|
2016-06-01 18:10:42 +08:00
|
|
|
state = acpi_lid_evaluate_state(device);
|
|
|
|
if (state < 0)
|
|
|
|
return state;
|
2009-09-11 06:28:02 +08:00
|
|
|
|
2018-06-28 01:55:02 +08:00
|
|
|
if (state && signal_wakeup)
|
|
|
|
acpi_pm_wakeup_event(&device->dev);
|
|
|
|
|
2016-06-01 18:10:42 +08:00
|
|
|
return acpi_lid_notify_state(device, state);
|
2007-10-22 18:18:18 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2016-06-01 18:10:48 +08:00
|
|
|
static void acpi_lid_initialize_state(struct acpi_device *device)
|
|
|
|
{
|
|
|
|
switch (lid_init_state) {
|
|
|
|
case ACPI_BUTTON_LID_INIT_OPEN:
|
|
|
|
(void)acpi_lid_notify_state(device, 1);
|
|
|
|
break;
|
2017-05-09 15:02:22 +08:00
|
|
|
case ACPI_BUTTON_LID_INIT_METHOD:
|
2018-06-28 01:55:02 +08:00
|
|
|
(void)acpi_lid_update_state(device, false);
|
2017-05-09 15:02:22 +08:00
|
|
|
break;
|
2016-06-01 18:10:48 +08:00
|
|
|
case ACPI_BUTTON_LID_INIT_IGNORE:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-31 01:48:18 +08:00
|
|
|
static void acpi_button_notify(struct acpi_device *device, u32 event)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-03-31 01:48:18 +08:00
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
2006-11-09 13:40:13 +08:00
|
|
|
struct input_dev *input;
|
2017-09-11 22:07:06 +08:00
|
|
|
int users;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
switch (event) {
|
2009-03-31 01:48:18 +08:00
|
|
|
case ACPI_FIXED_HARDWARE_EVENT:
|
|
|
|
event = ACPI_BUTTON_NOTIFY_STATUS;
|
|
|
|
/* fall through */
|
2005-04-17 06:20:36 +08:00
|
|
|
case ACPI_BUTTON_NOTIFY_STATUS:
|
2006-11-09 13:40:13 +08:00
|
|
|
input = button->input;
|
|
|
|
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
2017-09-11 22:07:06 +08:00
|
|
|
mutex_lock(&button->input->mutex);
|
|
|
|
users = button->input->users;
|
|
|
|
mutex_unlock(&button->input->mutex);
|
|
|
|
if (users)
|
2018-06-28 01:55:02 +08:00
|
|
|
acpi_lid_update_state(device, true);
|
2006-11-09 13:40:13 +08:00
|
|
|
} else {
|
2014-07-23 06:59:04 +08:00
|
|
|
int keycode;
|
|
|
|
|
ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle
The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ
during suspend-to-idle transitions and, consequently, any events
signaled through it wake up the system from that state. However,
on some systems some of the events signaled via the ACPI SCI while
suspended to idle should not cause the system to wake up. In fact,
quite often they should just be discarded.
Arguably, systems should not resume entirely on such events, but in
order to decide which events really should cause the system to resume
and which are spurious, it is necessary to resume up to the point
when ACPI SCIs are actually handled and processed, which is after
executing dpm_resume_noirq() in the system resume path.
For this reasons, add a loop around freeze_enter() in which the
platforms can process events signaled via multiplexed IRQ lines
like the ACPI SCI and add suspend-to-idle hooks that can be
used for this purpose to struct platform_freeze_ops.
In the ACPI case, the ->wake hook is used for checking if the SCI
has triggered while suspended and deferring the interrupt-induced
system wakeup until the events signaled through it are actually
processed sufficiently to decide whether or not the system should
resume. In turn, the ->sync hook allows all of the relevant event
queues to be flushed so as to prevent events from being missed due
to race conditions.
In addition to that, some ACPI code processing wakeup events needs
to be modified to use the "hard" version of wakeup triggers, so that
it will cause a system resume to happen on device-induced wakeup
events even if the "soft" mechanism to prevent the system from
suspending is not enabled. However, to preserve the existing
behavior with respect to suspend-to-RAM, this only is done in
the suspend-to-idle case and only if an SCI has occurred while
suspended.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2017-06-13 04:56:34 +08:00
|
|
|
acpi_pm_wakeup_event(&device->dev);
|
2014-07-23 06:59:04 +08:00
|
|
|
if (button->suspended)
|
|
|
|
break;
|
2006-11-09 13:40:13 +08:00
|
|
|
|
2014-07-23 06:59:04 +08:00
|
|
|
keycode = test_bit(KEY_SLEEP, input->keybit) ?
|
|
|
|
KEY_SLEEP : KEY_POWER;
|
2006-11-09 13:40:13 +08:00
|
|
|
input_report_key(input, keycode, 1);
|
|
|
|
input_sync(input);
|
|
|
|
input_report_key(input, keycode, 0);
|
2008-10-24 05:28:33 +08:00
|
|
|
input_sync(input);
|
2011-01-07 06:36:01 +08:00
|
|
|
|
2014-03-16 01:37:13 +08:00
|
|
|
acpi_bus_generate_netlink_event(
|
|
|
|
device->pnp.device_class,
|
|
|
|
dev_name(&device->dev),
|
|
|
|
event, ++button->pushed);
|
2006-11-09 13:40:13 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
2005-08-05 12:44:28 +08:00
|
|
|
"Unsupported event [0x%x]\n", event));
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-10 05:00:02 +08:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2014-07-23 06:59:04 +08:00
|
|
|
static int acpi_button_suspend(struct device *dev)
|
|
|
|
{
|
|
|
|
struct acpi_device *device = to_acpi_device(dev);
|
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
|
|
|
|
|
|
|
button->suspended = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-28 05:26:51 +08:00
|
|
|
static int acpi_button_resume(struct device *dev)
|
2007-10-22 18:18:18 +08:00
|
|
|
{
|
2012-06-28 05:26:51 +08:00
|
|
|
struct acpi_device *device = to_acpi_device(dev);
|
2009-04-08 23:39:49 +08:00
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
2009-04-08 23:39:38 +08:00
|
|
|
|
2014-07-23 06:59:04 +08:00
|
|
|
button->suspended = false;
|
2019-04-02 21:38:32 +08:00
|
|
|
if (button->type == ACPI_BUTTON_TYPE_LID && button->input->users) {
|
|
|
|
button->last_state = !!acpi_lid_evaluate_state(device);
|
|
|
|
button->last_time = ktime_get();
|
2016-06-01 18:10:48 +08:00
|
|
|
acpi_lid_initialize_state(device);
|
2019-04-02 21:38:32 +08:00
|
|
|
}
|
2007-10-22 18:18:18 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2012-08-10 05:00:02 +08:00
|
|
|
#endif
|
2007-10-22 18:18:18 +08:00
|
|
|
|
2017-09-11 22:07:06 +08:00
|
|
|
static int acpi_lid_input_open(struct input_dev *input)
|
|
|
|
{
|
|
|
|
struct acpi_device *device = input_get_drvdata(input);
|
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
|
|
|
|
|
|
|
button->last_state = !!acpi_lid_evaluate_state(device);
|
|
|
|
button->last_time = ktime_get();
|
|
|
|
acpi_lid_initialize_state(device);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
static int acpi_button_add(struct acpi_device *device)
|
|
|
|
{
|
|
|
|
struct acpi_button *button;
|
|
|
|
struct input_dev *input;
|
2010-10-01 16:54:00 +08:00
|
|
|
const char *hid = acpi_device_hid(device);
|
|
|
|
char *name, *class;
|
2009-04-08 23:39:49 +08:00
|
|
|
int error;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2017-11-22 23:06:12 +08:00
|
|
|
if (!strcmp(hid, ACPI_BUTTON_HID_LID) && dmi_check_system(lid_blacklst))
|
|
|
|
return -ENODEV;
|
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!button)
|
2006-06-27 12:41:40 +08:00
|
|
|
return -ENOMEM;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-09-23 05:37:34 +08:00
|
|
|
device->driver_data = button;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
button->input = input = input_allocate_device();
|
|
|
|
if (!input) {
|
|
|
|
error = -ENOMEM;
|
|
|
|
goto err_free_button;
|
|
|
|
}
|
|
|
|
|
2009-04-08 23:39:54 +08:00
|
|
|
name = acpi_device_name(device);
|
|
|
|
class = acpi_device_class(device);
|
|
|
|
|
2009-04-08 23:40:04 +08:00
|
|
|
if (!strcmp(hid, ACPI_BUTTON_HID_POWER) ||
|
|
|
|
!strcmp(hid, ACPI_BUTTON_HID_POWERF)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
button->type = ACPI_BUTTON_TYPE_POWER;
|
2009-04-08 23:39:54 +08:00
|
|
|
strcpy(name, ACPI_BUTTON_DEVICE_NAME_POWER);
|
|
|
|
sprintf(class, "%s/%s",
|
2005-04-17 06:20:36 +08:00
|
|
|
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
|
2009-04-08 23:40:04 +08:00
|
|
|
} else if (!strcmp(hid, ACPI_BUTTON_HID_SLEEP) ||
|
|
|
|
!strcmp(hid, ACPI_BUTTON_HID_SLEEPF)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
button->type = ACPI_BUTTON_TYPE_SLEEP;
|
2009-04-08 23:39:54 +08:00
|
|
|
strcpy(name, ACPI_BUTTON_DEVICE_NAME_SLEEP);
|
|
|
|
sprintf(class, "%s/%s",
|
2005-04-17 06:20:36 +08:00
|
|
|
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
|
2009-04-08 23:39:54 +08:00
|
|
|
} else if (!strcmp(hid, ACPI_BUTTON_HID_LID)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
button->type = ACPI_BUTTON_TYPE_LID;
|
2009-04-08 23:39:54 +08:00
|
|
|
strcpy(name, ACPI_BUTTON_DEVICE_NAME_LID);
|
|
|
|
sprintf(class, "%s/%s",
|
2005-04-17 06:20:36 +08:00
|
|
|
ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
|
2017-09-11 22:07:06 +08:00
|
|
|
input->open = acpi_lid_input_open;
|
2005-08-05 12:44:28 +08:00
|
|
|
} else {
|
2009-04-08 23:39:54 +08:00
|
|
|
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
|
2006-11-09 13:40:13 +08:00
|
|
|
error = -ENODEV;
|
|
|
|
goto err_free_input;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
error = acpi_button_add_fs(device);
|
|
|
|
if (error)
|
|
|
|
goto err_free_input;
|
|
|
|
|
2009-04-08 23:39:54 +08:00
|
|
|
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
|
2006-11-09 13:40:13 +08:00
|
|
|
|
2009-04-08 23:39:54 +08:00
|
|
|
input->name = name;
|
2006-11-09 13:40:13 +08:00
|
|
|
input->phys = button->phys;
|
|
|
|
input->id.bustype = BUS_HOST;
|
|
|
|
input->id.product = button->type;
|
2008-03-05 07:06:35 +08:00
|
|
|
input->dev.parent = &device->dev;
|
2005-08-04 05:55:21 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
switch (button->type) {
|
2006-11-09 13:40:13 +08:00
|
|
|
case ACPI_BUTTON_TYPE_POWER:
|
2013-09-12 15:32:03 +08:00
|
|
|
input_set_capability(input, EV_KEY, KEY_POWER);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2006-11-09 13:40:13 +08:00
|
|
|
|
|
|
|
case ACPI_BUTTON_TYPE_SLEEP:
|
2013-09-12 15:32:03 +08:00
|
|
|
input_set_capability(input, EV_KEY, KEY_SLEEP);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2006-11-09 13:40:13 +08:00
|
|
|
|
|
|
|
case ACPI_BUTTON_TYPE_LID:
|
2013-09-12 15:32:03 +08:00
|
|
|
input_set_capability(input, EV_SW, SW_LID);
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-09-11 22:07:06 +08:00
|
|
|
input_set_drvdata(input, device);
|
2006-11-09 13:40:13 +08:00
|
|
|
error = input_register_device(input);
|
|
|
|
if (error)
|
2009-03-31 01:48:18 +08:00
|
|
|
goto err_remove_fs;
|
2009-09-11 06:28:02 +08:00
|
|
|
if (button->type == ACPI_BUTTON_TYPE_LID) {
|
|
|
|
/*
|
|
|
|
* This assumes there's only one lid device, or if there are
|
|
|
|
* more we only care about the last one...
|
|
|
|
*/
|
|
|
|
lid_device = device;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
ACPI / PM: Ignore spurious SCI wakeups from suspend-to-idle
The ACPI SCI (System Control Interrupt) is set up as a wakeup IRQ
during suspend-to-idle transitions and, consequently, any events
signaled through it wake up the system from that state. However,
on some systems some of the events signaled via the ACPI SCI while
suspended to idle should not cause the system to wake up. In fact,
quite often they should just be discarded.
Arguably, systems should not resume entirely on such events, but in
order to decide which events really should cause the system to resume
and which are spurious, it is necessary to resume up to the point
when ACPI SCIs are actually handled and processed, which is after
executing dpm_resume_noirq() in the system resume path.
For this reasons, add a loop around freeze_enter() in which the
platforms can process events signaled via multiplexed IRQ lines
like the ACPI SCI and add suspend-to-idle hooks that can be
used for this purpose to struct platform_freeze_ops.
In the ACPI case, the ->wake hook is used for checking if the SCI
has triggered while suspended and deferring the interrupt-induced
system wakeup until the events signaled through it are actually
processed sufficiently to decide whether or not the system should
resume. In turn, the ->sync hook allows all of the relevant event
queues to be flushed so as to prevent events from being missed due
to race conditions.
In addition to that, some ACPI code processing wakeup events needs
to be modified to use the "hard" version of wakeup triggers, so that
it will cause a system resume to happen on device-induced wakeup
events even if the "soft" mechanism to prevent the system from
suspending is not enabled. However, to preserve the existing
behavior with respect to suspend-to-RAM, this only is done in
the suspend-to-idle case and only if an SCI has occurred while
suspended.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2017-06-13 04:56:34 +08:00
|
|
|
device_init_wakeup(&device->dev, true);
|
2009-04-08 23:39:54 +08:00
|
|
|
printk(KERN_INFO PREFIX "%s [%s]\n", name, acpi_device_bid(device));
|
2006-11-09 13:40:13 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-11-09 13:40:13 +08:00
|
|
|
err_remove_fs:
|
|
|
|
acpi_button_remove_fs(device);
|
|
|
|
err_free_input:
|
|
|
|
input_free_device(input);
|
|
|
|
err_free_button:
|
|
|
|
kfree(button);
|
|
|
|
return error;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-01-24 07:24:48 +08:00
|
|
|
static int acpi_button_remove(struct acpi_device *device)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2009-04-08 23:39:49 +08:00
|
|
|
struct acpi_button *button = acpi_driver_data(device);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-08-05 12:44:28 +08:00
|
|
|
acpi_button_remove_fs(device);
|
2006-11-09 13:40:13 +08:00
|
|
|
input_unregister_device(button->input);
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(button);
|
2006-06-27 12:41:40 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
treewide: Fix function prototypes for module_param_call()
Several function prototypes for the set/get functions defined by
module_param_call() have a slightly wrong argument types. This fixes
those in an effort to clean up the calls when running under type-enforced
compiler instrumentation for CFI. This is the result of running the
following semantic patch:
@match_module_param_call_function@
declarer name module_param_call;
identifier _name, _set_func, _get_func;
expression _arg, _mode;
@@
module_param_call(_name, _set_func, _get_func, _arg, _mode);
@fix_set_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._set_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _set_func(
-_val_type _val
+const char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
@fix_get_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._get_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _get_func(
-_val_type _val
+char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
Two additional by-hand changes are included for places where the above
Coccinelle script didn't notice them:
drivers/platform/x86/thinkpad_acpi.c
fs/lockd/svc.c
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
2017-10-18 10:04:42 +08:00
|
|
|
static int param_set_lid_init_state(const char *val,
|
|
|
|
const struct kernel_param *kp)
|
2016-06-01 18:10:48 +08:00
|
|
|
{
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (!strncmp(val, "open", sizeof("open") - 1)) {
|
|
|
|
lid_init_state = ACPI_BUTTON_LID_INIT_OPEN;
|
|
|
|
pr_info("Notify initial lid state as open\n");
|
2017-05-09 15:02:22 +08:00
|
|
|
} else if (!strncmp(val, "method", sizeof("method") - 1)) {
|
|
|
|
lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
|
|
|
|
pr_info("Notify initial lid state with _LID return value\n");
|
2016-06-01 18:10:48 +08:00
|
|
|
} else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) {
|
|
|
|
lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE;
|
|
|
|
pr_info("Do not notify initial lid state\n");
|
|
|
|
} else
|
|
|
|
result = -EINVAL;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
treewide: Fix function prototypes for module_param_call()
Several function prototypes for the set/get functions defined by
module_param_call() have a slightly wrong argument types. This fixes
those in an effort to clean up the calls when running under type-enforced
compiler instrumentation for CFI. This is the result of running the
following semantic patch:
@match_module_param_call_function@
declarer name module_param_call;
identifier _name, _set_func, _get_func;
expression _arg, _mode;
@@
module_param_call(_name, _set_func, _get_func, _arg, _mode);
@fix_set_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._set_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _set_func(
-_val_type _val
+const char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
@fix_get_prototype
depends on match_module_param_call_function@
identifier match_module_param_call_function._get_func;
identifier _val, _param;
type _val_type, _param_type;
@@
int _get_func(
-_val_type _val
+char * _val
,
-_param_type _param
+const struct kernel_param * _param
) { ... }
Two additional by-hand changes are included for places where the above
Coccinelle script didn't notice them:
drivers/platform/x86/thinkpad_acpi.c
fs/lockd/svc.c
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Jessica Yu <jeyu@kernel.org>
2017-10-18 10:04:42 +08:00
|
|
|
static int param_get_lid_init_state(char *buffer,
|
|
|
|
const struct kernel_param *kp)
|
2016-06-01 18:10:48 +08:00
|
|
|
{
|
|
|
|
switch (lid_init_state) {
|
|
|
|
case ACPI_BUTTON_LID_INIT_OPEN:
|
|
|
|
return sprintf(buffer, "open");
|
2017-05-09 15:02:22 +08:00
|
|
|
case ACPI_BUTTON_LID_INIT_METHOD:
|
|
|
|
return sprintf(buffer, "method");
|
2016-06-01 18:10:48 +08:00
|
|
|
case ACPI_BUTTON_LID_INIT_IGNORE:
|
|
|
|
return sprintf(buffer, "ignore");
|
|
|
|
default:
|
|
|
|
return sprintf(buffer, "invalid");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
module_param_call(lid_init_state,
|
|
|
|
param_set_lid_init_state, param_get_lid_init_state,
|
|
|
|
NULL, 0644);
|
|
|
|
MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state");
|
|
|
|
|
2018-04-23 17:16:56 +08:00
|
|
|
static int acpi_button_register_driver(struct acpi_driver *driver)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Modules such as nouveau.ko and i915.ko have a link time dependency
|
|
|
|
* on acpi_lid_open(), and would therefore not be loadable on ACPI
|
|
|
|
* capable kernels booted in non-ACPI mode if the return value of
|
|
|
|
* acpi_bus_register_driver() is returned from here with ACPI disabled
|
|
|
|
* when this driver is built as a module.
|
|
|
|
*/
|
|
|
|
if (acpi_disabled)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return acpi_bus_register_driver(driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void acpi_button_unregister_driver(struct acpi_driver *driver)
|
|
|
|
{
|
|
|
|
if (!acpi_disabled)
|
|
|
|
acpi_bus_unregister_driver(driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_driver(acpi_button_driver, acpi_button_register_driver,
|
|
|
|
acpi_button_unregister_driver);
|