.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * button.c - ACPI Button Driver |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> |
---|
5 | 6 | * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
---|
6 | | - * |
---|
7 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify |
---|
10 | | - * it under the terms of the GNU General Public License as published by |
---|
11 | | - * the Free Software Foundation; either version 2 of the License, or (at |
---|
12 | | - * your option) any later version. |
---|
13 | | - * |
---|
14 | | - * This program is distributed in the hope that it will be useful, but |
---|
15 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
17 | | - * General Public License for more details. |
---|
18 | | - * |
---|
19 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
20 | 7 | */ |
---|
21 | 8 | |
---|
22 | 9 | #define pr_fmt(fmt) "ACPI: button: " fmt |
---|
.. | .. |
---|
37 | 24 | #define PREFIX "ACPI: " |
---|
38 | 25 | |
---|
39 | 26 | #define ACPI_BUTTON_CLASS "button" |
---|
40 | | -#define ACPI_BUTTON_FILE_INFO "info" |
---|
41 | 27 | #define ACPI_BUTTON_FILE_STATE "state" |
---|
42 | 28 | #define ACPI_BUTTON_TYPE_UNKNOWN 0x00 |
---|
43 | 29 | #define ACPI_BUTTON_NOTIFY_STATUS 0x80 |
---|
44 | 30 | |
---|
45 | 31 | #define ACPI_BUTTON_SUBCLASS_POWER "power" |
---|
46 | | -#define ACPI_BUTTON_HID_POWER "PNP0C0C" |
---|
47 | 32 | #define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button" |
---|
48 | 33 | #define ACPI_BUTTON_TYPE_POWER 0x01 |
---|
49 | 34 | |
---|
50 | 35 | #define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" |
---|
51 | | -#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" |
---|
52 | 36 | #define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button" |
---|
53 | 37 | #define ACPI_BUTTON_TYPE_SLEEP 0x03 |
---|
54 | 38 | |
---|
55 | 39 | #define ACPI_BUTTON_SUBCLASS_LID "lid" |
---|
56 | | -#define ACPI_BUTTON_HID_LID "PNP0C0D" |
---|
57 | 40 | #define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" |
---|
58 | 41 | #define ACPI_BUTTON_TYPE_LID 0x05 |
---|
59 | 42 | |
---|
60 | | -#define ACPI_BUTTON_LID_INIT_IGNORE 0x00 |
---|
61 | | -#define ACPI_BUTTON_LID_INIT_OPEN 0x01 |
---|
62 | | -#define ACPI_BUTTON_LID_INIT_METHOD 0x02 |
---|
| 43 | +enum { |
---|
| 44 | + ACPI_BUTTON_LID_INIT_IGNORE, |
---|
| 45 | + ACPI_BUTTON_LID_INIT_OPEN, |
---|
| 46 | + ACPI_BUTTON_LID_INIT_METHOD, |
---|
| 47 | + ACPI_BUTTON_LID_INIT_DISABLED, |
---|
| 48 | +}; |
---|
| 49 | + |
---|
| 50 | +static const char * const lid_init_state_str[] = { |
---|
| 51 | + [ACPI_BUTTON_LID_INIT_IGNORE] = "ignore", |
---|
| 52 | + [ACPI_BUTTON_LID_INIT_OPEN] = "open", |
---|
| 53 | + [ACPI_BUTTON_LID_INIT_METHOD] = "method", |
---|
| 54 | + [ACPI_BUTTON_LID_INIT_DISABLED] = "disabled", |
---|
| 55 | +}; |
---|
63 | 56 | |
---|
64 | 57 | #define _COMPONENT ACPI_BUTTON_COMPONENT |
---|
65 | 58 | ACPI_MODULE_NAME("button"); |
---|
.. | .. |
---|
78 | 71 | }; |
---|
79 | 72 | MODULE_DEVICE_TABLE(acpi, button_device_ids); |
---|
80 | 73 | |
---|
81 | | -/* |
---|
82 | | - * Some devices which don't even have a lid in anyway have a broken _LID |
---|
83 | | - * method (e.g. pointing to a floating gpio pin) causing spurious LID events. |
---|
84 | | - */ |
---|
85 | | -static const struct dmi_system_id lid_blacklst[] = { |
---|
| 74 | +/* Please keep this list sorted alphabetically by vendor and model */ |
---|
| 75 | +static const struct dmi_system_id dmi_lid_quirks[] = { |
---|
86 | 76 | { |
---|
87 | | - /* GP-electronic T701 */ |
---|
| 77 | + /* GP-electronic T701, _LID method points to a floating GPIO */ |
---|
88 | 78 | .matches = { |
---|
89 | 79 | DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), |
---|
90 | 80 | DMI_MATCH(DMI_PRODUCT_NAME, "T701"), |
---|
91 | 81 | DMI_MATCH(DMI_BIOS_VERSION, "BYT70A.YNCHENG.WIN.007"), |
---|
92 | 82 | }, |
---|
| 83 | + .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, |
---|
| 84 | + }, |
---|
| 85 | + { |
---|
| 86 | + /* Nextbook Ares 8A tablet, _LID device always reports lid closed */ |
---|
| 87 | + .matches = { |
---|
| 88 | + DMI_MATCH(DMI_SYS_VENDOR, "Insyde"), |
---|
| 89 | + DMI_MATCH(DMI_PRODUCT_NAME, "CherryTrail"), |
---|
| 90 | + DMI_MATCH(DMI_BIOS_VERSION, "M882"), |
---|
| 91 | + }, |
---|
| 92 | + .driver_data = (void *)(long)ACPI_BUTTON_LID_INIT_DISABLED, |
---|
93 | 93 | }, |
---|
94 | 94 | { |
---|
95 | 95 | /* |
---|
.. | .. |
---|
163 | 163 | bool lid_state_initialized; |
---|
164 | 164 | }; |
---|
165 | 165 | |
---|
166 | | -static BLOCKING_NOTIFIER_HEAD(acpi_lid_notifier); |
---|
167 | 166 | static struct acpi_device *lid_device; |
---|
168 | | -static u8 lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
---|
| 167 | +static long lid_init_state = -1; |
---|
169 | 168 | |
---|
170 | 169 | static unsigned long lid_report_interval __read_mostly = 500; |
---|
171 | 170 | module_param(lid_report_interval, ulong, 0644); |
---|
.. | .. |
---|
193 | 192 | static int acpi_lid_notify_state(struct acpi_device *device, int state) |
---|
194 | 193 | { |
---|
195 | 194 | struct acpi_button *button = acpi_driver_data(device); |
---|
196 | | - int ret; |
---|
197 | 195 | ktime_t next_report; |
---|
198 | 196 | bool do_update; |
---|
199 | 197 | |
---|
.. | .. |
---|
270 | 268 | button->last_time = ktime_get(); |
---|
271 | 269 | } |
---|
272 | 270 | |
---|
273 | | - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, device); |
---|
274 | | - if (ret == NOTIFY_DONE) |
---|
275 | | - ret = blocking_notifier_call_chain(&acpi_lid_notifier, state, |
---|
276 | | - device); |
---|
277 | | - if (ret == NOTIFY_DONE || ret == NOTIFY_OK) { |
---|
278 | | - /* |
---|
279 | | - * It is also regarded as success if the notifier_chain |
---|
280 | | - * returns NOTIFY_OK or NOTIFY_DONE. |
---|
281 | | - */ |
---|
282 | | - ret = 0; |
---|
283 | | - } |
---|
284 | | - return ret; |
---|
| 271 | + return 0; |
---|
285 | 272 | } |
---|
286 | 273 | |
---|
287 | 274 | static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, |
---|
.. | .. |
---|
378 | 365 | /* -------------------------------------------------------------------------- |
---|
379 | 366 | Driver Interface |
---|
380 | 367 | -------------------------------------------------------------------------- */ |
---|
381 | | -int acpi_lid_notifier_register(struct notifier_block *nb) |
---|
382 | | -{ |
---|
383 | | - return blocking_notifier_chain_register(&acpi_lid_notifier, nb); |
---|
384 | | -} |
---|
385 | | -EXPORT_SYMBOL(acpi_lid_notifier_register); |
---|
386 | | - |
---|
387 | | -int acpi_lid_notifier_unregister(struct notifier_block *nb) |
---|
388 | | -{ |
---|
389 | | - return blocking_notifier_chain_unregister(&acpi_lid_notifier, nb); |
---|
390 | | -} |
---|
391 | | -EXPORT_SYMBOL(acpi_lid_notifier_unregister); |
---|
392 | | - |
---|
393 | 368 | int acpi_lid_open(void) |
---|
394 | 369 | { |
---|
395 | 370 | if (!lid_device) |
---|
.. | .. |
---|
441 | 416 | switch (event) { |
---|
442 | 417 | case ACPI_FIXED_HARDWARE_EVENT: |
---|
443 | 418 | event = ACPI_BUTTON_NOTIFY_STATUS; |
---|
444 | | - /* fall through */ |
---|
| 419 | + fallthrough; |
---|
445 | 420 | case ACPI_BUTTON_NOTIFY_STATUS: |
---|
446 | 421 | input = button->input; |
---|
447 | 422 | if (button->type == ACPI_BUTTON_TYPE_LID) { |
---|
.. | .. |
---|
519 | 494 | char *name, *class; |
---|
520 | 495 | int error; |
---|
521 | 496 | |
---|
522 | | - if (!strcmp(hid, ACPI_BUTTON_HID_LID) && dmi_check_system(lid_blacklst)) |
---|
| 497 | + if (!strcmp(hid, ACPI_BUTTON_HID_LID) && |
---|
| 498 | + lid_init_state == ACPI_BUTTON_LID_INIT_DISABLED) |
---|
523 | 499 | return -ENODEV; |
---|
524 | 500 | |
---|
525 | 501 | button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL); |
---|
.. | .. |
---|
625 | 601 | static int param_set_lid_init_state(const char *val, |
---|
626 | 602 | const struct kernel_param *kp) |
---|
627 | 603 | { |
---|
628 | | - int result = 0; |
---|
| 604 | + int i; |
---|
629 | 605 | |
---|
630 | | - if (!strncmp(val, "open", sizeof("open") - 1)) { |
---|
631 | | - lid_init_state = ACPI_BUTTON_LID_INIT_OPEN; |
---|
632 | | - pr_info("Notify initial lid state as open\n"); |
---|
633 | | - } else if (!strncmp(val, "method", sizeof("method") - 1)) { |
---|
634 | | - lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
---|
635 | | - pr_info("Notify initial lid state with _LID return value\n"); |
---|
636 | | - } else if (!strncmp(val, "ignore", sizeof("ignore") - 1)) { |
---|
637 | | - lid_init_state = ACPI_BUTTON_LID_INIT_IGNORE; |
---|
638 | | - pr_info("Do not notify initial lid state\n"); |
---|
639 | | - } else |
---|
640 | | - result = -EINVAL; |
---|
641 | | - return result; |
---|
| 606 | + i = sysfs_match_string(lid_init_state_str, val); |
---|
| 607 | + if (i < 0) |
---|
| 608 | + return i; |
---|
| 609 | + |
---|
| 610 | + lid_init_state = i; |
---|
| 611 | + pr_info("Initial lid state set to '%s'\n", lid_init_state_str[i]); |
---|
| 612 | + return 0; |
---|
642 | 613 | } |
---|
643 | 614 | |
---|
644 | | -static int param_get_lid_init_state(char *buffer, |
---|
645 | | - const struct kernel_param *kp) |
---|
| 615 | +static int param_get_lid_init_state(char *buf, const struct kernel_param *kp) |
---|
646 | 616 | { |
---|
647 | | - switch (lid_init_state) { |
---|
648 | | - case ACPI_BUTTON_LID_INIT_OPEN: |
---|
649 | | - return sprintf(buffer, "open"); |
---|
650 | | - case ACPI_BUTTON_LID_INIT_METHOD: |
---|
651 | | - return sprintf(buffer, "method"); |
---|
652 | | - case ACPI_BUTTON_LID_INIT_IGNORE: |
---|
653 | | - return sprintf(buffer, "ignore"); |
---|
654 | | - default: |
---|
655 | | - return sprintf(buffer, "invalid"); |
---|
656 | | - } |
---|
657 | | - return 0; |
---|
| 617 | + int i, c = 0; |
---|
| 618 | + |
---|
| 619 | + for (i = 0; i < ARRAY_SIZE(lid_init_state_str); i++) |
---|
| 620 | + if (i == lid_init_state) |
---|
| 621 | + c += sprintf(buf + c, "[%s] ", lid_init_state_str[i]); |
---|
| 622 | + else |
---|
| 623 | + c += sprintf(buf + c, "%s ", lid_init_state_str[i]); |
---|
| 624 | + |
---|
| 625 | + buf[c - 1] = '\n'; /* Replace the final space with a newline */ |
---|
| 626 | + |
---|
| 627 | + return c; |
---|
658 | 628 | } |
---|
659 | 629 | |
---|
660 | 630 | module_param_call(lid_init_state, |
---|
.. | .. |
---|
664 | 634 | |
---|
665 | 635 | static int acpi_button_register_driver(struct acpi_driver *driver) |
---|
666 | 636 | { |
---|
| 637 | + const struct dmi_system_id *dmi_id; |
---|
| 638 | + |
---|
| 639 | + if (lid_init_state == -1) { |
---|
| 640 | + dmi_id = dmi_first_match(dmi_lid_quirks); |
---|
| 641 | + if (dmi_id) |
---|
| 642 | + lid_init_state = (long)dmi_id->driver_data; |
---|
| 643 | + else |
---|
| 644 | + lid_init_state = ACPI_BUTTON_LID_INIT_METHOD; |
---|
| 645 | + } |
---|
| 646 | + |
---|
667 | 647 | /* |
---|
668 | 648 | * Modules such as nouveau.ko and i915.ko have a link time dependency |
---|
669 | 649 | * on acpi_lid_open(), and would therefore not be loadable on ACPI |
---|