Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
13bb0fd5 HG |
2 | /* |
3 | * PEAQ 2-in-1 WMI hotkey driver | |
4 | * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> | |
13bb0fd5 HG |
5 | */ |
6 | ||
7 | #include <linux/acpi.h> | |
3b952061 | 8 | #include <linux/dmi.h> |
13bb0fd5 HG |
9 | #include <linux/input-polldev.h> |
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | ||
13 | #define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000" | |
14 | #define PEAQ_DOLBY_BUTTON_METHOD_ID 5 | |
15 | #define PEAQ_POLL_INTERVAL_MS 250 | |
16 | #define PEAQ_POLL_IGNORE_MS 500 | |
17 | #define PEAQ_POLL_MAX_MS 1000 | |
18 | ||
19 | MODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); | |
20 | ||
21 | static unsigned int peaq_ignore_events_counter; | |
22 | static struct input_polled_dev *peaq_poll_dev; | |
23 | ||
24 | /* | |
25 | * The Dolby button (yes really a Dolby button) causes an ACPI variable to get | |
26 | * set on both press and release. The WMI method checks and clears that flag. | |
27 | * So for a press + release we will get back One from the WMI method either once | |
28 | * (if polling after the release) or twice (polling between press and release). | |
29 | * We ignore events for 0.5s after the first event to avoid reporting 2 presses. | |
30 | */ | |
31 | static void peaq_wmi_poll(struct input_polled_dev *dev) | |
32 | { | |
33 | union acpi_object obj; | |
34 | acpi_status status; | |
35 | u32 dummy = 0; | |
36 | ||
37 | struct acpi_buffer input = { sizeof(dummy), &dummy }; | |
38 | struct acpi_buffer output = { sizeof(obj), &obj }; | |
39 | ||
2cf7bdec | 40 | status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0, |
13bb0fd5 HG |
41 | PEAQ_DOLBY_BUTTON_METHOD_ID, |
42 | &input, &output); | |
43 | if (ACPI_FAILURE(status)) | |
44 | return; | |
45 | ||
46 | if (obj.type != ACPI_TYPE_INTEGER) { | |
47 | dev_err(&peaq_poll_dev->input->dev, | |
48 | "Error WMBC did not return an integer\n"); | |
49 | return; | |
50 | } | |
51 | ||
890f658c | 52 | if (peaq_ignore_events_counter && peaq_ignore_events_counter--) |
13bb0fd5 HG |
53 | return; |
54 | ||
55 | if (obj.integer.value) { | |
56 | input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 1); | |
57 | input_sync(peaq_poll_dev->input); | |
58 | input_event(peaq_poll_dev->input, EV_KEY, KEY_SOUND, 0); | |
59 | input_sync(peaq_poll_dev->input); | |
60 | peaq_ignore_events_counter = max(1u, | |
61 | PEAQ_POLL_IGNORE_MS / peaq_poll_dev->poll_interval); | |
62 | } | |
63 | } | |
64 | ||
3b952061 | 65 | /* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ |
f6c7b803 | 66 | static const struct dmi_system_id peaq_dmi_table[] __initconst = { |
3b952061 HG |
67 | { |
68 | .matches = { | |
69 | DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), | |
70 | DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), | |
71 | }, | |
72 | }, | |
d6fa71f1 | 73 | {} |
3b952061 HG |
74 | }; |
75 | ||
13bb0fd5 HG |
76 | static int __init peaq_wmi_init(void) |
77 | { | |
3b952061 HG |
78 | /* WMI GUID is not unique, also check for a DMI match */ |
79 | if (!dmi_check_system(peaq_dmi_table)) | |
80 | return -ENODEV; | |
81 | ||
dfea7e18 | 82 | if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) |
13bb0fd5 HG |
83 | return -ENODEV; |
84 | ||
85 | peaq_poll_dev = input_allocate_polled_device(); | |
86 | if (!peaq_poll_dev) | |
87 | return -ENOMEM; | |
88 | ||
89 | peaq_poll_dev->poll = peaq_wmi_poll; | |
90 | peaq_poll_dev->poll_interval = PEAQ_POLL_INTERVAL_MS; | |
91 | peaq_poll_dev->poll_interval_max = PEAQ_POLL_MAX_MS; | |
92 | peaq_poll_dev->input->name = "PEAQ WMI hotkeys"; | |
93 | peaq_poll_dev->input->phys = "wmi/input0"; | |
94 | peaq_poll_dev->input->id.bustype = BUS_HOST; | |
95 | input_set_capability(peaq_poll_dev->input, EV_KEY, KEY_SOUND); | |
96 | ||
97 | return input_register_polled_device(peaq_poll_dev); | |
98 | } | |
99 | ||
100 | static void __exit peaq_wmi_exit(void) | |
101 | { | |
13bb0fd5 HG |
102 | input_unregister_polled_device(peaq_poll_dev); |
103 | } | |
104 | ||
105 | module_init(peaq_wmi_init); | |
106 | module_exit(peaq_wmi_exit); | |
107 | ||
108 | MODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver"); | |
109 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | |
110 | MODULE_LICENSE("GPL"); |