18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PEAQ 2-in-1 WMI hotkey driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Hans de Goede <hdegoede@redhat.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/acpi.h> 88c2ecf20Sopenharmony_ci#include <linux/dmi.h> 98c2ecf20Sopenharmony_ci#include <linux/input.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000" 148c2ecf20Sopenharmony_ci#define PEAQ_DOLBY_BUTTON_METHOD_ID 5 158c2ecf20Sopenharmony_ci#define PEAQ_POLL_INTERVAL_MS 250 168c2ecf20Sopenharmony_ci#define PEAQ_POLL_IGNORE_MS 500 178c2ecf20Sopenharmony_ci#define PEAQ_POLL_MAX_MS 1000 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:"PEAQ_DOLBY_BUTTON_GUID); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic struct input_dev *peaq_poll_dev; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * The Dolby button (yes really a Dolby button) causes an ACPI variable to get 258c2ecf20Sopenharmony_ci * set on both press and release. The WMI method checks and clears that flag. 268c2ecf20Sopenharmony_ci * So for a press + release we will get back One from the WMI method either once 278c2ecf20Sopenharmony_ci * (if polling after the release) or twice (polling between press and release). 288c2ecf20Sopenharmony_ci * We ignore events for 0.5s after the first event to avoid reporting 2 presses. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_cistatic void peaq_wmi_poll(struct input_dev *input_dev) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci static unsigned long last_event_time; 338c2ecf20Sopenharmony_ci static bool had_events; 348c2ecf20Sopenharmony_ci union acpi_object obj; 358c2ecf20Sopenharmony_ci acpi_status status; 368c2ecf20Sopenharmony_ci u32 dummy = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci struct acpi_buffer input = { sizeof(dummy), &dummy }; 398c2ecf20Sopenharmony_ci struct acpi_buffer output = { sizeof(obj), &obj }; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci status = wmi_evaluate_method(PEAQ_DOLBY_BUTTON_GUID, 0, 428c2ecf20Sopenharmony_ci PEAQ_DOLBY_BUTTON_METHOD_ID, 438c2ecf20Sopenharmony_ci &input, &output); 448c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 458c2ecf20Sopenharmony_ci return; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (obj.type != ACPI_TYPE_INTEGER) { 488c2ecf20Sopenharmony_ci dev_err(&input_dev->dev, 498c2ecf20Sopenharmony_ci "Error WMBC did not return an integer\n"); 508c2ecf20Sopenharmony_ci return; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (!obj.integer.value) 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (had_events && time_before(jiffies, last_event_time + 578c2ecf20Sopenharmony_ci msecs_to_jiffies(PEAQ_POLL_IGNORE_MS))) 588c2ecf20Sopenharmony_ci return; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci input_event(input_dev, EV_KEY, KEY_SOUND, 1); 618c2ecf20Sopenharmony_ci input_sync(input_dev); 628c2ecf20Sopenharmony_ci input_event(input_dev, EV_KEY, KEY_SOUND, 0); 638c2ecf20Sopenharmony_ci input_sync(input_dev); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci last_event_time = jiffies; 668c2ecf20Sopenharmony_ci had_events = true; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */ 708c2ecf20Sopenharmony_cistatic const struct dmi_system_id peaq_dmi_table[] __initconst = { 718c2ecf20Sopenharmony_ci { 728c2ecf20Sopenharmony_ci .matches = { 738c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), 748c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), 758c2ecf20Sopenharmony_ci }, 768c2ecf20Sopenharmony_ci }, 778c2ecf20Sopenharmony_ci {} 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic int __init peaq_wmi_init(void) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci int err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* WMI GUID is not unique, also check for a DMI match */ 858c2ecf20Sopenharmony_ci if (!dmi_check_system(peaq_dmi_table)) 868c2ecf20Sopenharmony_ci return -ENODEV; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (!wmi_has_guid(PEAQ_DOLBY_BUTTON_GUID)) 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci peaq_poll_dev = input_allocate_device(); 928c2ecf20Sopenharmony_ci if (!peaq_poll_dev) 938c2ecf20Sopenharmony_ci return -ENOMEM; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci peaq_poll_dev->name = "PEAQ WMI hotkeys"; 968c2ecf20Sopenharmony_ci peaq_poll_dev->phys = "wmi/input0"; 978c2ecf20Sopenharmony_ci peaq_poll_dev->id.bustype = BUS_HOST; 988c2ecf20Sopenharmony_ci input_set_capability(peaq_poll_dev, EV_KEY, KEY_SOUND); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci err = input_setup_polling(peaq_poll_dev, peaq_wmi_poll); 1018c2ecf20Sopenharmony_ci if (err) 1028c2ecf20Sopenharmony_ci goto err_out; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci input_set_poll_interval(peaq_poll_dev, PEAQ_POLL_INTERVAL_MS); 1058c2ecf20Sopenharmony_ci input_set_max_poll_interval(peaq_poll_dev, PEAQ_POLL_MAX_MS); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci err = input_register_device(peaq_poll_dev); 1088c2ecf20Sopenharmony_ci if (err) 1098c2ecf20Sopenharmony_ci goto err_out; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cierr_out: 1148c2ecf20Sopenharmony_ci input_free_device(peaq_poll_dev); 1158c2ecf20Sopenharmony_ci return err; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void __exit peaq_wmi_exit(void) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci input_unregister_device(peaq_poll_dev); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cimodule_init(peaq_wmi_init); 1248c2ecf20Sopenharmony_cimodule_exit(peaq_wmi_exit); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PEAQ 2-in-1 WMI hotkey driver"); 1278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 1288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 129