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