mirror of
https://github.com/nxp-imx/linux-imx.git
synced 2025-09-03 02:16:09 +02:00

[ Upstream commit 151e78a0b8
]
The LGEX0815 ACPI device is used by the "LG Airplane Mode Button"
Windows driver for handling rfkill requests. When the ACPI device
receives an 0x80 ACPI notification, an rfkill event is to be
send to userspace.
Add support for the LGEX0815 ACPI device to the driver.
Tested-by: Agathe Boutmy <agathe@boutmy.com>
Signed-off-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20240606233540.9774-2-W_Armin@gmx.de
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
125 lines
2.6 KiB
C
125 lines
2.6 KiB
C
// 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:*");
|
|
MODULE_ALIAS("acpi*:LGEX0815:*");
|
|
|
|
struct wl_button {
|
|
struct input_dev *input_dev;
|
|
char phys[32];
|
|
};
|
|
|
|
static const struct acpi_device_id wl_ids[] = {
|
|
{"HPQ6001", 0},
|
|
{"WSTADEF", 0},
|
|
{"AMDI0051", 0},
|
|
{"LGEX0815", 0},
|
|
{"", 0},
|
|
};
|
|
|
|
static int wireless_input_setup(struct acpi_device *device)
|
|
{
|
|
struct wl_button *button = acpi_driver_data(device);
|
|
int err;
|
|
|
|
button->input_dev = input_allocate_device();
|
|
if (!button->input_dev)
|
|
return -ENOMEM;
|
|
|
|
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);
|
|
|
|
err = input_register_device(button->input_dev);
|
|
if (err)
|
|
goto err_free_dev;
|
|
|
|
return 0;
|
|
|
|
err_free_dev:
|
|
input_free_device(button->input_dev);
|
|
return err;
|
|
}
|
|
|
|
static void wireless_input_destroy(struct acpi_device *device)
|
|
{
|
|
struct wl_button *button = acpi_driver_data(device);
|
|
|
|
input_unregister_device(button->input_dev);
|
|
kfree(button);
|
|
}
|
|
|
|
static void wl_notify(struct acpi_device *acpi_dev, u32 event)
|
|
{
|
|
struct wl_button *button = acpi_driver_data(acpi_dev);
|
|
|
|
if (event != 0x80) {
|
|
pr_info("Received unknown event (0x%x)\n", event);
|
|
return;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static int wl_add(struct acpi_device *device)
|
|
{
|
|
struct wl_button *button;
|
|
int err;
|
|
|
|
button = kzalloc(sizeof(struct wl_button), GFP_KERNEL);
|
|
if (!button)
|
|
return -ENOMEM;
|
|
|
|
device->driver_data = button;
|
|
|
|
err = wireless_input_setup(device);
|
|
if (err) {
|
|
pr_err("Failed to setup wireless hotkeys\n");
|
|
kfree(button);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void wl_remove(struct acpi_device *device)
|
|
{
|
|
wireless_input_destroy(device);
|
|
}
|
|
|
|
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);
|