1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Airplane mode button for HP & Xiaomi laptops
4 *
5 * Copyright (C) 2014-2017 Alex Hung <alex.hung@canonical.com>
6 */
7
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/init.h>
11 #include <linux/input.h>
12 #include <linux/platform_device.h>
13 #include <linux/acpi.h>
14 #include <acpi/acpi_bus.h>
15
16 MODULE_LICENSE("GPL");
17 MODULE_AUTHOR("Alex Hung");
18 MODULE_ALIAS("acpi*:HPQ6001:*");
19 MODULE_ALIAS("acpi*:WSTADEF:*");
20 MODULE_ALIAS("acpi*:AMDI0051:*");
21
22 static struct input_dev *hpwl_input_dev;
23
24 static const struct acpi_device_id hpwl_ids[] = {
25 {"HPQ6001", 0},
26 {"WSTADEF", 0},
27 {"AMDI0051", 0},
28 {"", 0},
29 };
30
hp_wireless_input_setup(void)31 static int hp_wireless_input_setup(void)
32 {
33 int err;
34
35 hpwl_input_dev = input_allocate_device();
36 if (!hpwl_input_dev)
37 return -ENOMEM;
38
39 hpwl_input_dev->name = "HP Wireless hotkeys";
40 hpwl_input_dev->phys = "hpq6001/input0";
41 hpwl_input_dev->id.bustype = BUS_HOST;
42 hpwl_input_dev->evbit[0] = BIT(EV_KEY);
43 set_bit(KEY_RFKILL, hpwl_input_dev->keybit);
44
45 err = input_register_device(hpwl_input_dev);
46 if (err)
47 goto err_free_dev;
48
49 return 0;
50
51 err_free_dev:
52 input_free_device(hpwl_input_dev);
53 return err;
54 }
55
hp_wireless_input_destroy(void)56 static void hp_wireless_input_destroy(void)
57 {
58 input_unregister_device(hpwl_input_dev);
59 }
60
hpwl_notify(struct acpi_device * acpi_dev,u32 event)61 static void hpwl_notify(struct acpi_device *acpi_dev, u32 event)
62 {
63 if (event != 0x80) {
64 pr_info("Received unknown event (0x%x)\n", event);
65 return;
66 }
67
68 input_report_key(hpwl_input_dev, KEY_RFKILL, 1);
69 input_sync(hpwl_input_dev);
70 input_report_key(hpwl_input_dev, KEY_RFKILL, 0);
71 input_sync(hpwl_input_dev);
72 }
73
hpwl_add(struct acpi_device * device)74 static int hpwl_add(struct acpi_device *device)
75 {
76 int err;
77
78 err = hp_wireless_input_setup();
79 if (err)
80 pr_err("Failed to setup hp wireless hotkeys\n");
81
82 return err;
83 }
84
hpwl_remove(struct acpi_device * device)85 static int hpwl_remove(struct acpi_device *device)
86 {
87 hp_wireless_input_destroy();
88 return 0;
89 }
90
91 static struct acpi_driver hpwl_driver = {
92 .name = "hp-wireless",
93 .owner = THIS_MODULE,
94 .ids = hpwl_ids,
95 .ops = {
96 .add = hpwl_add,
97 .remove = hpwl_remove,
98 .notify = hpwl_notify,
99 },
100 };
101
102 module_acpi_driver(hpwl_driver);
103