xref: /linux/drivers/platform/x86/uniwill/uniwill-wmi.c (revision 9d588a1140b9ae211581a7a154d0b806d8cd8238)
1*d0504796SArmin Wolf // SPDX-License-Identifier: GPL-2.0-or-later
2*d0504796SArmin Wolf /*
3*d0504796SArmin Wolf  * Linux hotkey driver for Uniwill notebooks.
4*d0504796SArmin Wolf  *
5*d0504796SArmin Wolf  * Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach
6*d0504796SArmin Wolf  * for supporting the development of this driver either through prior work or
7*d0504796SArmin Wolf  * by answering questions regarding the underlying WMI interface.
8*d0504796SArmin Wolf  *
9*d0504796SArmin Wolf  * Copyright (C) 2025 Armin Wolf <W_Armin@gmx.de>
10*d0504796SArmin Wolf  */
11*d0504796SArmin Wolf 
12*d0504796SArmin Wolf #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13*d0504796SArmin Wolf 
14*d0504796SArmin Wolf #include <linux/acpi.h>
15*d0504796SArmin Wolf #include <linux/device.h>
16*d0504796SArmin Wolf #include <linux/init.h>
17*d0504796SArmin Wolf #include <linux/mod_devicetable.h>
18*d0504796SArmin Wolf #include <linux/notifier.h>
19*d0504796SArmin Wolf #include <linux/printk.h>
20*d0504796SArmin Wolf #include <linux/types.h>
21*d0504796SArmin Wolf #include <linux/wmi.h>
22*d0504796SArmin Wolf 
23*d0504796SArmin Wolf #include "uniwill-wmi.h"
24*d0504796SArmin Wolf 
25*d0504796SArmin Wolf #define DRIVER_NAME		"uniwill-wmi"
26*d0504796SArmin Wolf #define UNIWILL_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
27*d0504796SArmin Wolf 
28*d0504796SArmin Wolf static BLOCKING_NOTIFIER_HEAD(uniwill_wmi_chain_head);
29*d0504796SArmin Wolf 
devm_uniwill_wmi_unregister_notifier(void * data)30*d0504796SArmin Wolf static void devm_uniwill_wmi_unregister_notifier(void *data)
31*d0504796SArmin Wolf {
32*d0504796SArmin Wolf 	struct notifier_block *nb = data;
33*d0504796SArmin Wolf 
34*d0504796SArmin Wolf 	blocking_notifier_chain_unregister(&uniwill_wmi_chain_head, nb);
35*d0504796SArmin Wolf }
36*d0504796SArmin Wolf 
devm_uniwill_wmi_register_notifier(struct device * dev,struct notifier_block * nb)37*d0504796SArmin Wolf int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb)
38*d0504796SArmin Wolf {
39*d0504796SArmin Wolf 	int ret;
40*d0504796SArmin Wolf 
41*d0504796SArmin Wolf 	ret = blocking_notifier_chain_register(&uniwill_wmi_chain_head, nb);
42*d0504796SArmin Wolf 	if (ret < 0)
43*d0504796SArmin Wolf 		return ret;
44*d0504796SArmin Wolf 
45*d0504796SArmin Wolf 	return devm_add_action_or_reset(dev, devm_uniwill_wmi_unregister_notifier, nb);
46*d0504796SArmin Wolf }
47*d0504796SArmin Wolf 
uniwill_wmi_notify(struct wmi_device * wdev,union acpi_object * obj)48*d0504796SArmin Wolf static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
49*d0504796SArmin Wolf {
50*d0504796SArmin Wolf 	u32 value;
51*d0504796SArmin Wolf 
52*d0504796SArmin Wolf 	if (obj->type != ACPI_TYPE_INTEGER)
53*d0504796SArmin Wolf 		return;
54*d0504796SArmin Wolf 
55*d0504796SArmin Wolf 	value = obj->integer.value;
56*d0504796SArmin Wolf 
57*d0504796SArmin Wolf 	dev_dbg(&wdev->dev, "Received WMI event %u\n", value);
58*d0504796SArmin Wolf 
59*d0504796SArmin Wolf 	blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL);
60*d0504796SArmin Wolf }
61*d0504796SArmin Wolf 
62*d0504796SArmin Wolf /*
63*d0504796SArmin Wolf  * We cannot fully trust this GUID since Uniwill just copied the WMI GUID
64*d0504796SArmin Wolf  * from the Windows driver example, and others probably did the same.
65*d0504796SArmin Wolf  *
66*d0504796SArmin Wolf  * Because of this we cannot use this WMI GUID for autoloading. Instead the
67*d0504796SArmin Wolf  * associated driver will be registered manually after matching a DMI table.
68*d0504796SArmin Wolf  */
69*d0504796SArmin Wolf static const struct wmi_device_id uniwill_wmi_id_table[] = {
70*d0504796SArmin Wolf 	{ UNIWILL_EVENT_GUID, NULL },
71*d0504796SArmin Wolf 	{ }
72*d0504796SArmin Wolf };
73*d0504796SArmin Wolf 
74*d0504796SArmin Wolf static struct wmi_driver uniwill_wmi_driver = {
75*d0504796SArmin Wolf 	.driver = {
76*d0504796SArmin Wolf 		.name = DRIVER_NAME,
77*d0504796SArmin Wolf 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
78*d0504796SArmin Wolf 	},
79*d0504796SArmin Wolf 	.id_table = uniwill_wmi_id_table,
80*d0504796SArmin Wolf 	.notify = uniwill_wmi_notify,
81*d0504796SArmin Wolf 	.no_singleton = true,
82*d0504796SArmin Wolf };
83*d0504796SArmin Wolf 
uniwill_wmi_register_driver(void)84*d0504796SArmin Wolf int __init uniwill_wmi_register_driver(void)
85*d0504796SArmin Wolf {
86*d0504796SArmin Wolf 	return wmi_driver_register(&uniwill_wmi_driver);
87*d0504796SArmin Wolf }
88*d0504796SArmin Wolf 
uniwill_wmi_unregister_driver(void)89*d0504796SArmin Wolf void __exit uniwill_wmi_unregister_driver(void)
90*d0504796SArmin Wolf {
91*d0504796SArmin Wolf 	wmi_driver_unregister(&uniwill_wmi_driver);
92*d0504796SArmin Wolf }
93