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"
|
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"
|
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_DEVICE_NAME_LID "Lid Switch"
|
|
|
|
#define ACPI_BUTTON_TYPE_LID 0x05
|
|
|
|
|
2019-10-27 04:24:31 +08:00
|
|
|
enum {
|
|
|
|
ACPI_BUTTON_LID_INIT_IGNORE,
|
|
|
|
ACPI_BUTTON_LID_INIT_OPEN,
|
|
|
|
ACPI_BUTTON_LID_INIT_METHOD,
|
2019-10-27 04:24:32 +08:00
|
|
|
ACPI_BUTTON_LID_INIT_DISABLED,
|
2019-10-27 04:24:31 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const char * const lid_init_state_str[] = {
|
|
|
|
[ACPI_BUTTON_LID_INIT_IGNORE] = "ignore",
|
|
|
|
[ACPI_BUTTON_LID_INIT_OPEN] = "open",
|
|
|
|
[ACPI_BUTTON_LID_INIT_METHOD] = "method",
|
2019-10-27 04:24:32 +08:00
|
|
|
[ACPI_BUTTON_LID_INIT_DISABLED] = "disabled",
|
2019-10-27 04:24:31 +08:00
|
|
|
};
|
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);
|
|
|
|
|
2019-10-27 04:24:33 +08:00
|
|
|
/* Please keep this list sorted alphabetically by vendor and model */
|
|
|
|
static const struct dmi_system_id dmi_lid_quirks[] = {
|
ACPI: button: Add DMI quirk for Acer Switch 10 SW5-032 lid-switch
The Acer Switch 10 SW5-032 _LID method is quite broken, it looks like this:
Method (_LID, 0, NotSerialized) // _LID: Lid Status
{
If ((STAS & One))
{
Local0 = One
PBCG |= 0x05000000
HMCG |= 0x05000000
}
Else
{
Local0 = Zero
PBCG &= 0xF0FFFFFF
HMCG &= 0xF0FFFFFF
}
^^PCI0.GFX0.CLID = Local0
Return (Local0)
}
The problem here is the accesses to the PBCG and HMCG, these are the
pinconf0 registers for the power, resp. the home button GPIO,
e.g. PBCG is declared as:
OperationRegion (PWBT, SystemMemory, 0xFED0E080, 0x10)
Field (PWBT, DWordAcc, NoLock, Preserve)
{
PBCG, 32,
PBV1, 32,
PBSA, 32,
PBV2, 32
}
Where 0xFED0E000 is the base address of the GPO2 device and 0x80 is
the offset for the pin used for the powerbutton.
The problem here is this line in _LID:
PBCG |= 0x05000000
This changes the trigger flags of the GPIO, changing when it generates
interrupts. Note it does not clear the original flags. Linux uses an
edge triggered interrupt on both positive and negative edges. This |=
adds the BYT_TRIG_LVL flag to this, so now it is turned into a level
interrupt which fires both when low and high, iow it simply always
fires leading to an interrupt storm, the tablet immediately waking up
from suspend again, etc.
There is nothing we can do to fix this, except for a DSDT override,
which the user needs to do manually. The only thing we can do is
never call _LID, which requires disabling the lid-switch functionality
altogether.
This commit adds a quirk for this, as no lid-switch function is better
then the interrupt storm. A user manually applying a DSDT override can
also override the quirk on the kernel cmdline.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-11-18 23:35:56 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Acer Switch 10 SW5-012. _LID method messes with home and
|
|
|
|
* power button GPIO IRQ settings causing an interrupt storm on
|
|
|
|
* both GPIOs. This is unfixable without a DSDT override, so we
|
|
|
|
* have to disable the lid-switch functionality altogether :|
|
|
|
|
*/
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
|
|
|
|
},
|
|
|
|
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
|
|
|
|
},
|
2019-10-27 04:24:35 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Asus T200TA, _LID keeps reporting closed after every second
|
|
|
|
* openening of the lid. Causing immediate re-suspend after
|
|
|
|
* opening every other open. Using LID_INIT_OPEN fixes this.
|
|
|
|
*/
|
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "T200TA"),
|
|
|
|
},
|
|
|
|
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_OPEN,
|
|
|
|
},
|
2017-11-22 23:06:12 +08:00
|
|
|
{
|
2019-10-27 04:24:33 +08:00
|
|
|
/* GP-electronic T701, _LID method points to a floating GPIO */
|
2017-11-22 23:06:12 +08:00
|
|
|
.matches = {
|
|
|
|
DMI_MATCH(DMI_SYS_VENDOR, "Insyde"),
|
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "T701"),
|
|
|
|
DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"),
|
|
|
|
},
|
2019-10-27 04:24:33 +08:00
|
|
|
.driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED,
|
2017-11-22 23:06:12 +08:00
|
|
|
},
|
2019-10-27 04:24:34 +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,
|
|
|
|
},
|
2020-01-03 04:27:54 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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 struct acpi_device *lid_device;
|
2019-10-27 04:24:33 +08:00
|
|
|
static long lid_init_state = -1;
|
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);
|
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
|
|
|
|
2019-10-27 04:24:36 +08:00
|
|
|
return 0;
|
2016-06-01 18:10:42 +08:00
|
|
|
}
|
|
|
|
|
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_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
|
|
|
|
2019-10-27 04:24:32 +08:00
|
|
|
if (!strcmp(hid, ACPI_BUTTON_HID_LID) &&
|
2019-10-27 04:24:33 +08:00
|
|
|
lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED)
|
2017-11-22 23:06:12 +08:00
|
|
|
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
|
|
|
{
|
2019-10-27 04:24:31 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
i = sysfs_match_string(lid_init_state_str, val);
|
|
|
|
if (i < 0)
|
|
|
|
return i;
|
|
|
|
|
|
|
|
lid_init_state = i;
|
|
|
|
pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]);
|
|
|
|
return 0;
|
2016-06-01 18:10:48 +08:00
|
|
|
}
|
|
|
|
|
2019-10-27 04:24:31 +08:00
|
|
|
static int param_get_lid_init_state(char *buf, const struct kernel_param *kp)
|
2016-06-01 18:10:48 +08:00
|
|
|
{
|
2019-10-27 04:24:31 +08:00
|
|
|
int i, c = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++)
|
|
|
|
if (i == lid_init_state)
|
|
|
|
c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]);
|
|
|
|
else
|
|
|
|
c += sprintf(buf + c, "%s ", lid_init_state_str[i]);
|
|
|
|
|
|
|
|
buf[c - 1] = '\n'; /* Replace the final space with a newline */
|
|
|
|
|
|
|
|
return c;
|
2016-06-01 18:10:48 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2019-10-27 04:24:33 +08:00
|
|
|
const struct dmi_system_id *dmi_id;
|
|
|
|
|
|
|
|
if (lid_init_state == -1) {
|
|
|
|
dmi_id = dmi_first_match(dmi_lid_quirks);
|
|
|
|
if (dmi_id)
|
|
|
|
lid_init_state = (long)dmi_id->driver_data;
|
|
|
|
else
|
|
|
|
lid_init_state = ACPI_BUTTON_LID_INIT_METHOD;
|
|
|
|
}
|
|
|
|
|
2018-04-23 17:16:56 +08:00
|
|
|
/*
|
|
|
|
* 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);
|