2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2017-05-12 17:11:54 +02:00
/*
* PEAQ 2 - in - 1 WMI hotkey driver
* Copyright ( C ) 2017 Hans de Goede < hdegoede @ redhat . com >
*/
# include <linux/acpi.h>
2017-10-05 20:04:04 +02:00
# include <linux/dmi.h>
2019-10-01 11:58:22 -07:00
# include <linux/input.h>
2017-05-12 17:11:54 +02:00
# include <linux/kernel.h>
# include <linux/module.h>
# define PEAQ_DOLBY_BUTTON_GUID "ABBC0F6F-8EA1-11D1-00A0-C90629100000"
# define PEAQ_DOLBY_BUTTON_METHOD_ID 5
# define PEAQ_POLL_INTERVAL_MS 250
# define PEAQ_POLL_IGNORE_MS 500
# define PEAQ_POLL_MAX_MS 1000
MODULE_ALIAS ( " wmi: " PEAQ_DOLBY_BUTTON_GUID ) ;
2019-10-01 11:58:22 -07:00
static struct input_dev * peaq_poll_dev ;
2017-05-12 17:11:54 +02:00
/*
* The Dolby button ( yes really a Dolby button ) causes an ACPI variable to get
* set on both press and release . The WMI method checks and clears that flag .
* So for a press + release we will get back One from the WMI method either once
* ( if polling after the release ) or twice ( polling between press and release ) .
* We ignore events for 0.5 s after the first event to avoid reporting 2 presses .
*/
2019-10-01 11:58:22 -07:00
static void peaq_wmi_poll ( struct input_dev * input_dev )
2017-05-12 17:11:54 +02:00
{
2019-10-01 11:58:22 -07:00
static unsigned long last_event_time ;
static bool had_events ;
2017-05-12 17:11:54 +02:00
union acpi_object obj ;
acpi_status status ;
u32 dummy = 0 ;
struct acpi_buffer input = { sizeof ( dummy ) , & dummy } ;
struct acpi_buffer output = { sizeof ( obj ) , & obj } ;
2017-08-12 09:44:17 +02:00
status = wmi_evaluate_method ( PEAQ_DOLBY_BUTTON_GUID , 0 ,
2017-05-12 17:11:54 +02:00
PEAQ_DOLBY_BUTTON_METHOD_ID ,
& input , & output ) ;
if ( ACPI_FAILURE ( status ) )
return ;
if ( obj . type ! = ACPI_TYPE_INTEGER ) {
2019-10-01 11:58:22 -07:00
dev_err ( & input_dev - > dev ,
2017-05-12 17:11:54 +02:00
" Error WMBC did not return an integer \n " ) ;
return ;
}
2019-10-01 11:58:22 -07:00
if ( ! obj . integer . value )
2017-05-12 17:11:54 +02:00
return ;
2019-10-01 11:58:22 -07:00
if ( had_events & & time_before ( jiffies , last_event_time +
msecs_to_jiffies ( PEAQ_POLL_IGNORE_MS ) ) )
return ;
input_event ( input_dev , EV_KEY , KEY_SOUND , 1 ) ;
input_sync ( input_dev ) ;
input_event ( input_dev , EV_KEY , KEY_SOUND , 0 ) ;
input_sync ( input_dev ) ;
last_event_time = jiffies ;
had_events = true ;
2017-05-12 17:11:54 +02:00
}
2017-10-05 20:04:04 +02:00
/* Some other devices (Shuttle XS35) use the same WMI GUID for other purposes */
2017-10-30 14:07:38 +01:00
static const struct dmi_system_id peaq_dmi_table [ ] __initconst = {
2017-10-05 20:04:04 +02:00
{
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " PEAQ " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PEAQ PMM C1010 MD99187 " ) ,
} ,
} ,
2017-10-30 14:07:37 +01:00
{ }
2017-10-05 20:04:04 +02:00
} ;
2017-05-12 17:11:54 +02:00
static int __init peaq_wmi_init ( void )
{
2019-10-01 11:58:22 -07:00
int err ;
2017-10-05 20:04:04 +02:00
/* WMI GUID is not unique, also check for a DMI match */
if ( ! dmi_check_system ( peaq_dmi_table ) )
return - ENODEV ;
2017-10-08 21:04:43 +03:00
if ( ! wmi_has_guid ( PEAQ_DOLBY_BUTTON_GUID ) )
2017-05-12 17:11:54 +02:00
return - ENODEV ;
2019-10-01 11:58:22 -07:00
peaq_poll_dev = input_allocate_device ( ) ;
2017-05-12 17:11:54 +02:00
if ( ! peaq_poll_dev )
return - ENOMEM ;
2019-10-01 11:58:22 -07:00
peaq_poll_dev - > name = " PEAQ WMI hotkeys " ;
peaq_poll_dev - > phys = " wmi/input0 " ;
peaq_poll_dev - > id . bustype = BUS_HOST ;
input_set_capability ( peaq_poll_dev , EV_KEY , KEY_SOUND ) ;
err = input_setup_polling ( peaq_poll_dev , peaq_wmi_poll ) ;
if ( err )
goto err_out ;
input_set_poll_interval ( peaq_poll_dev , PEAQ_POLL_INTERVAL_MS ) ;
input_set_max_poll_interval ( peaq_poll_dev , PEAQ_POLL_MAX_MS ) ;
err = input_register_device ( peaq_poll_dev ) ;
if ( err )
goto err_out ;
return 0 ;
2017-05-12 17:11:54 +02:00
2019-10-01 11:58:22 -07:00
err_out :
input_free_device ( peaq_poll_dev ) ;
return err ;
2017-05-12 17:11:54 +02:00
}
static void __exit peaq_wmi_exit ( void )
{
2019-10-01 11:58:22 -07:00
input_unregister_device ( peaq_poll_dev ) ;
2017-05-12 17:11:54 +02:00
}
module_init ( peaq_wmi_init ) ;
module_exit ( peaq_wmi_exit ) ;
MODULE_DESCRIPTION ( " PEAQ 2-in-1 WMI hotkey driver " ) ;
MODULE_AUTHOR ( " Hans de Goede <hdegoede@redhat.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;