2021-05-19 12:44:05 -05:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Airplane mode button for AMD , HP & Xiaomi laptops
*
* Copyright ( C ) 2014 - 2017 Alex Hung < alex . hung @ canonical . com >
* Copyright ( C ) 2021 Advanced Micro Devices
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/input.h>
# include <linux/platform_device.h>
# include <linux/acpi.h>
# include <acpi/acpi_bus.h>
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Alex Hung " ) ;
MODULE_ALIAS ( " acpi*:HPQ6001:* " ) ;
MODULE_ALIAS ( " acpi*:WSTADEF:* " ) ;
MODULE_ALIAS ( " acpi*:AMDI0051:* " ) ;
2022-11-26 17:46:17 -07:00
struct wl_button {
struct input_dev * input_dev ;
char phys [ 32 ] ;
} ;
2021-05-19 12:44:05 -05:00
static const struct acpi_device_id wl_ids [ ] = {
{ " HPQ6001 " , 0 } ,
{ " WSTADEF " , 0 } ,
{ " AMDI0051 " , 0 } ,
{ " " , 0 } ,
} ;
2022-11-26 17:46:17 -07:00
static int wireless_input_setup ( struct acpi_device * device )
2021-05-19 12:44:05 -05:00
{
2022-11-26 17:46:17 -07:00
struct wl_button * button = acpi_driver_data ( device ) ;
2021-05-19 12:44:05 -05:00
int err ;
2022-11-26 17:46:17 -07:00
button - > input_dev = input_allocate_device ( ) ;
if ( ! button - > input_dev )
2021-05-19 12:44:05 -05:00
return - ENOMEM ;
2022-11-26 17:46:17 -07:00
snprintf ( button - > phys , sizeof ( button - > phys ) , " %s/input0 " , acpi_device_hid ( device ) ) ;
button - > input_dev - > name = " Wireless hotkeys " ;
button - > input_dev - > phys = button - > phys ;
button - > input_dev - > id . bustype = BUS_HOST ;
button - > input_dev - > evbit [ 0 ] = BIT ( EV_KEY ) ;
set_bit ( KEY_RFKILL , button - > input_dev - > keybit ) ;
2021-05-19 12:44:05 -05:00
2022-11-26 17:46:17 -07:00
err = input_register_device ( button - > input_dev ) ;
2021-05-19 12:44:05 -05:00
if ( err )
goto err_free_dev ;
return 0 ;
err_free_dev :
2022-11-26 17:46:17 -07:00
input_free_device ( button - > input_dev ) ;
2021-05-19 12:44:05 -05:00
return err ;
}
2022-11-26 17:46:17 -07:00
static void wireless_input_destroy ( struct acpi_device * device )
2021-05-19 12:44:05 -05:00
{
2022-11-26 17:46:17 -07:00
struct wl_button * button = acpi_driver_data ( device ) ;
input_unregister_device ( button - > input_dev ) ;
kfree ( button ) ;
2021-05-19 12:44:05 -05:00
}
static void wl_notify ( struct acpi_device * acpi_dev , u32 event )
{
2022-11-26 17:46:17 -07:00
struct wl_button * button = acpi_driver_data ( acpi_dev ) ;
2021-05-19 12:44:05 -05:00
if ( event ! = 0x80 ) {
pr_info ( " Received unknown event (0x%x) \n " , event ) ;
return ;
}
2022-11-26 17:46:17 -07:00
input_report_key ( button - > input_dev , KEY_RFKILL , 1 ) ;
input_sync ( button - > input_dev ) ;
input_report_key ( button - > input_dev , KEY_RFKILL , 0 ) ;
input_sync ( button - > input_dev ) ;
2021-05-19 12:44:05 -05:00
}
static int wl_add ( struct acpi_device * device )
{
2022-11-26 17:46:17 -07:00
struct wl_button * button ;
2021-05-19 12:44:05 -05:00
int err ;
2022-11-26 17:46:17 -07:00
button = kzalloc ( sizeof ( struct wl_button ) , GFP_KERNEL ) ;
if ( ! button )
return - ENOMEM ;
device - > driver_data = button ;
err = wireless_input_setup ( device ) ;
if ( err ) {
2021-07-10 13:08:10 -06:00
pr_err ( " Failed to setup wireless hotkeys \n " ) ;
2022-11-26 17:46:17 -07:00
kfree ( button ) ;
}
2021-05-19 12:44:05 -05:00
return err ;
}
2022-11-14 00:26:09 +08:00
static void wl_remove ( struct acpi_device * device )
2021-05-19 12:44:05 -05:00
{
2022-11-26 17:46:17 -07:00
wireless_input_destroy ( device ) ;
2021-05-19 12:44:05 -05:00
}
static struct acpi_driver wl_driver = {
. name = " wireless-hotkey " ,
. owner = THIS_MODULE ,
. ids = wl_ids ,
. ops = {
. add = wl_add ,
. remove = wl_remove ,
. notify = wl_notify ,
} ,
} ;
module_acpi_driver ( wl_driver ) ;