// SPDX-License-Identifier: GPL-2.0-or-later /* * Linux hotkey driver for Uniwill notebooks. * * Special thanks go to Pőcze Barnabás, Christoffer Sandberg and Werner Sembach * for supporting the development of this driver either through prior work or * by answering questions regarding the underlying WMI interface. * * Copyright (C) 2025 Armin Wolf */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include "uniwill-wmi.h" #define DRIVER_NAME "uniwill-wmi" #define UNIWILL_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000" static BLOCKING_NOTIFIER_HEAD(uniwill_wmi_chain_head); static void devm_uniwill_wmi_unregister_notifier(void *data) { struct notifier_block *nb = data; blocking_notifier_chain_unregister(&uniwill_wmi_chain_head, nb); } int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb) { int ret; ret = blocking_notifier_chain_register(&uniwill_wmi_chain_head, nb); if (ret < 0) return ret; return devm_add_action_or_reset(dev, devm_uniwill_wmi_unregister_notifier, nb); } static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) { u32 value; if (obj->type != ACPI_TYPE_INTEGER) return; value = obj->integer.value; dev_dbg(&wdev->dev, "Received WMI event %u\n", value); blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); } /* * We cannot fully trust this GUID since Uniwill just copied the WMI GUID * from the Windows driver example, and others probably did the same. * * Because of this we cannot use this WMI GUID for autoloading. Instead the * associated driver will be registered manually after matching a DMI table. */ static const struct wmi_device_id uniwill_wmi_id_table[] = { { UNIWILL_EVENT_GUID, NULL }, { } }; static struct wmi_driver uniwill_wmi_driver = { .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = uniwill_wmi_id_table, .notify = uniwill_wmi_notify, .no_singleton = true, }; int __init uniwill_wmi_register_driver(void) { return wmi_driver_register(&uniwill_wmi_driver); } void __exit uniwill_wmi_unregister_driver(void) { wmi_driver_unregister(&uniwill_wmi_driver); }