1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * ACPI Direct App Launch driver
5 * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
6 * Copyright (C) 2022 Arvid Norlander <lkml@vorapal.se>
7 * Copyright (C) 2007-2010 Angelo Arrifano <miknix@gmail.com>
9 * Information gathered from disassembled dsdt and from here:
10 * <https://archive.org/details/microsoft-acpi-dirapplaunch>
13 #include <linux/acpi.h>
14 #include <linux/device.h>
15 #include <linux/errno.h>
16 #include <linux/init.h>
17 #include <linux/input.h>
18 #include <linux/input/sparse-keymap.h>
19 #include <linux/mod_devicetable.h>
20 #include <linux/module.h>
21 #include <linux/mutex.h>
22 #include <linux/platform_device.h>
23 #include <linux/pm_wakeup.h>
24 #include <linux/printk.h>
25 #include <linux/slab.h>
26 #include <linux/sysfs.h>
27 #include <linux/types.h>
29 #include <linux/unaligned.h>
31 #define DRIVER_NAME "quickstart"
34 * There will be two events:
35 * 0x02 - Button was pressed while device was off/sleeping.
36 * 0x80 - Button was pressed while device was up.
38 #define QUICKSTART_EVENT_RUNTIME 0x80
40 struct quickstart_data
{
42 struct mutex input_lock
; /* Protects input sequence during notify */
43 struct input_dev
*input_device
;
50 * Knowing what these buttons do require system specific knowledge.
51 * This could be done by matching on DMI data in a long quirk table.
52 * However, it is easier to leave it up to user space to figure this out.
54 * Using for example udev hwdb the scancode 0x1 can be remapped suitably.
56 static const struct key_entry quickstart_keymap
[] = {
57 { KE_KEY
, 0x1, { KEY_UNKNOWN
} },
61 static ssize_t
button_id_show(struct device
*dev
, struct device_attribute
*attr
, char *buf
)
63 struct quickstart_data
*data
= dev_get_drvdata(dev
);
65 return sysfs_emit(buf
, "%u\n", data
->id
);
67 static DEVICE_ATTR_RO(button_id
);
69 static struct attribute
*quickstart_attrs
[] = {
70 &dev_attr_button_id
.attr
,
73 ATTRIBUTE_GROUPS(quickstart
);
75 static void quickstart_notify(acpi_handle handle
, u32 event
, void *context
)
77 struct quickstart_data
*data
= context
;
80 case QUICKSTART_EVENT_RUNTIME
:
81 mutex_lock(&data
->input_lock
);
82 sparse_keymap_report_event(data
->input_device
, 0x1, 1, true);
83 mutex_unlock(&data
->input_lock
);
85 acpi_bus_generate_netlink_event(DRIVER_NAME
, dev_name(data
->dev
), event
, 0);
88 dev_err(data
->dev
, FW_INFO
"Unexpected ACPI notify event (%u)\n", event
);
94 * The GHID ACPI method is used to indicate the "role" of the button.
95 * However, all the meanings of these values are vendor defined.
97 * We do however expose this value to user space.
99 static int quickstart_get_ghid(struct quickstart_data
*data
)
101 struct acpi_buffer buffer
= { ACPI_ALLOCATE_BUFFER
, NULL
};
102 acpi_handle handle
= ACPI_HANDLE(data
->dev
);
103 union acpi_object
*obj
;
108 * This returns a buffer telling the button usage ID,
109 * and triggers pending notify events (The ones before booting).
111 status
= acpi_evaluate_object_typed(handle
, "GHID", NULL
, &buffer
, ACPI_TYPE_BUFFER
);
112 if (ACPI_FAILURE(status
))
115 obj
= buffer
.pointer
;
120 * Quoting the specification:
121 * "The GHID method can return a BYTE, WORD, or DWORD.
122 * The value must be encoded in little-endian byte
123 * order (least significant byte first)."
125 switch (obj
->buffer
.length
) {
127 data
->id
= obj
->buffer
.pointer
[0];
130 data
->id
= get_unaligned_le16(obj
->buffer
.pointer
);
133 data
->id
= get_unaligned_le32(obj
->buffer
.pointer
);
137 FW_BUG
"GHID method returned buffer of unexpected length %u\n",
148 static void quickstart_notify_remove(void *context
)
150 struct quickstart_data
*data
= context
;
153 handle
= ACPI_HANDLE(data
->dev
);
155 acpi_remove_notify_handler(handle
, ACPI_DEVICE_NOTIFY
, quickstart_notify
);
158 static void quickstart_mutex_destroy(void *data
)
160 struct mutex
*lock
= data
;
165 static int quickstart_probe(struct platform_device
*pdev
)
167 struct quickstart_data
*data
;
172 handle
= ACPI_HANDLE(&pdev
->dev
);
176 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
180 data
->dev
= &pdev
->dev
;
181 dev_set_drvdata(&pdev
->dev
, data
);
183 mutex_init(&data
->input_lock
);
184 ret
= devm_add_action_or_reset(&pdev
->dev
, quickstart_mutex_destroy
, &data
->input_lock
);
189 * We have to initialize the device wakeup before evaluating GHID because
190 * doing so will notify the device if the button was used to wake the machine
193 device_init_wakeup(&pdev
->dev
, true);
195 ret
= quickstart_get_ghid(data
);
199 data
->input_device
= devm_input_allocate_device(&pdev
->dev
);
200 if (!data
->input_device
)
203 ret
= sparse_keymap_setup(data
->input_device
, quickstart_keymap
, NULL
);
207 snprintf(data
->input_name
, sizeof(data
->input_name
), "Quickstart Button %u", data
->id
);
208 snprintf(data
->phys
, sizeof(data
->phys
), DRIVER_NAME
"/input%u", data
->id
);
210 data
->input_device
->name
= data
->input_name
;
211 data
->input_device
->phys
= data
->phys
;
212 data
->input_device
->id
.bustype
= BUS_HOST
;
214 ret
= input_register_device(data
->input_device
);
218 status
= acpi_install_notify_handler(handle
, ACPI_DEVICE_NOTIFY
, quickstart_notify
, data
);
219 if (ACPI_FAILURE(status
))
222 return devm_add_action_or_reset(&pdev
->dev
, quickstart_notify_remove
, data
);
225 static const struct acpi_device_id quickstart_device_ids
[] = {
229 MODULE_DEVICE_TABLE(acpi
, quickstart_device_ids
);
231 static struct platform_driver quickstart_platform_driver
= {
234 .dev_groups
= quickstart_groups
,
235 .probe_type
= PROBE_PREFER_ASYNCHRONOUS
,
236 .acpi_match_table
= quickstart_device_ids
,
238 .probe
= quickstart_probe
,
240 module_platform_driver(quickstart_platform_driver
);
242 MODULE_AUTHOR("Armin Wolf <W_Armin@gmx.de>");
243 MODULE_AUTHOR("Arvid Norlander <lkml@vorpal.se>");
244 MODULE_AUTHOR("Angelo Arrifano");
245 MODULE_DESCRIPTION("ACPI Direct App Launch driver");
246 MODULE_LICENSE("GPL");