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