lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <77da5151-ddbd-409f-8b48-28029a96d5d8@kernel.org>
Date: Mon, 16 Jun 2025 11:21:26 +0200
From: Hans de Goede <hansg@...nel.org>
To: Armin Wolf <W_Armin@....de>, ilpo.jarvinen@...ux.intel.com,
 hdegoede@...hat.com, chumuzero@...il.com, corbet@....net, cs@...edo.de,
 wse@...edocomputers.com, ggo@...edocomputers.com
Cc: linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org,
 platform-driver-x86@...r.kernel.org
Subject: Re: [RFC PATCH 1/3] platform/x86: Add Uniwill WMI driver

On 15-Jun-25 19:59, Armin Wolf wrote:
> Add a new driver for handling WMI events on Uniwill laptops.
> The driver sadly cannot use the WMI GUID for autoloading since Uniwill
> just copied it from the Windows driver example.
> 
> The driver is reverse-engineered based on the following information:
> - https://github.com/pobrn/qc71_laptop
> - https://github.com/tuxedocomputers/tuxedo-drivers
> - various OEM software
> 
> Reported-by: cyear <chumuzero@...il.com>
> Closes: https://github.com/lm-sensors/lm-sensors/issues/508
> Closes: https://github.com/Wer-Wolf/uniwill-laptop/issues/3
> Signed-off-by: Armin Wolf <W_Armin@....de>
> ---
>  Documentation/wmi/devices/uniwill-wmi.rst  |  52 ++++++
>  MAINTAINERS                                |   8 +
>  drivers/platform/x86/Kconfig               |   2 +
>  drivers/platform/x86/Makefile              |   3 +
>  drivers/platform/x86/uniwill/Kconfig       |  32 ++++
>  drivers/platform/x86/uniwill/Makefile      |   7 +
>  drivers/platform/x86/uniwill/uniwill-wmi.c | 177 +++++++++++++++++++++
>  drivers/platform/x86/uniwill/uniwill-wmi.h | 122 ++++++++++++++
>  8 files changed, 403 insertions(+)
>  create mode 100644 Documentation/wmi/devices/uniwill-wmi.rst
>  create mode 100644 drivers/platform/x86/uniwill/Kconfig
>  create mode 100644 drivers/platform/x86/uniwill/Makefile
>  create mode 100644 drivers/platform/x86/uniwill/uniwill-wmi.c
>  create mode 100644 drivers/platform/x86/uniwill/uniwill-wmi.h
> 
> diff --git a/Documentation/wmi/devices/uniwill-wmi.rst b/Documentation/wmi/devices/uniwill-wmi.rst
> new file mode 100644
> index 000000000000..232fa8349611
> --- /dev/null
> +++ b/Documentation/wmi/devices/uniwill-wmi.rst
> @@ -0,0 +1,52 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +======================================
> +Uniwill WMI event driver (uniwill-wmi)
> +======================================
> +
> +Introduction
> +============
> +
> +Many notebooks manufactured by Uniwill (either directly or as ODM) provide an WMI-based
> +event interface for various platform events like hotkeys. This interface is used by the
> +``uniwill-wmi`` driver to react to hotkey presses.
> +
> +WMI interface description
> +=========================
> +
> +The WMI interface description can be decoded from the embedded binary MOF (bmof)
> +data using the `bmfdec <https://github.com/pali/bmfdec>`_ utility:
> +
> +::
> +
> +  [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x409"),
> +   Description("Class containing event generated ULong data"),
> +   guid("{ABBC0F72-8EA1-11d1-00A0-C90629100000}")]
> +  class AcpiTest_EventULong : WmiEvent {
> +    [key, read] string InstanceName;
> +    [read] boolean Active;
> +
> +    [WmiDataId(1), read, write, Description("ULong Data")] uint32 ULong;
> +  };
> +
> +Most of the WMI-related code was copied from the Windows driver samples, which unfortunately means
> +that the WMI-GUID is not unique. This makes the WMI-GUID unusable for autoloading.
> +
> +WMI event data
> +--------------
> +
> +The WMI event data contains a single 32-bit value which is used to indicate various platform events.
> +Many non-hotkey events are not directly consumed by the driver itself, but are instead handled by
> +the ``uniwill-laptop`` driver.
> +
> +Reverse-Engineering the Uniwill WMI event interface
> +===================================================
> +
> +The driver logs debug messages when receiving a WMI event. Thus enabling debug messages will be
> +useful for finding unknown event codes.
> +
> +Special thanks go to github user `pobrn` which developed the
> +`qc71_laptop <https://github.com/pobrn/qc71_laptop>`_ driver on which this driver is partly based.
> +The same is true for Tuxedo Computers, which developed the
> +`tuxedo-drivers <https://github.com/tuxedocomputers/tuxedo-drivers>`_ package which also served as
> +a foundation for this driver.
> diff --git a/MAINTAINERS b/MAINTAINERS
> index c14614613377..53876ec2d111 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -25496,6 +25496,14 @@ L:	linux-scsi@...r.kernel.org
>  S:	Maintained
>  F:	drivers/ufs/host/ufs-renesas.c
>  
> +UNIWILL WMI DRIVER
> +M:	Armin Wolf <W_Armin@....de>
> +L:	platform-driver-x86@...r.kernel.org
> +S:	Maintained
> +F:	Documentation/wmi/devices/uniwill-wmi.rst
> +F:	drivers/platform/x86/uniwill/uniwill-wmi.c
> +F:	drivers/platform/x86/uniwill/uniwill-wmi.h
> +
>  UNSORTED BLOCK IMAGES (UBI)
>  M:	Richard Weinberger <richard@....at>
>  R:	Zhihao Cheng <chengzhihao1@...wei.com>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 43055df44827..ba9d65f01332 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -65,6 +65,8 @@ config HUAWEI_WMI
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called huawei-wmi.
>  
> +source "drivers/platform/x86/uniwill/Kconfig"
> +
>  config UV_SYSFS
>  	tristate "Sysfs structure for UV systems"
>  	depends on X86_UV
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 0530a224bebd..1549c56ced91 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -107,6 +107,9 @@ obj-$(CONFIG_TOSHIBA_WMI)	+= toshiba-wmi.o
>  # before toshiba_acpi initializes
>  obj-$(CONFIG_ACPI_TOSHIBA)	+= toshiba_acpi.o
>  
> +# Uniwill
> +obj-y				+= uniwill/
> +
>  # Inspur
>  obj-$(CONFIG_INSPUR_PLATFORM_PROFILE)	+= inspur_platform_profile.o
>  
> diff --git a/drivers/platform/x86/uniwill/Kconfig b/drivers/platform/x86/uniwill/Kconfig
> new file mode 100644
> index 000000000000..5f1ea3e9e72f
> --- /dev/null
> +++ b/drivers/platform/x86/uniwill/Kconfig
> @@ -0,0 +1,32 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Uniwill X86 Platform Specific Drivers
> +#
> +
> +menuconfig X86_PLATFORM_DRIVERS_UNIWILL
> +	bool "Uniwill X86 Platform Specific Device Drivers"
> +	depends on X86_PLATFORM_DEVICES
> +	help
> +	  Say Y here to get to see options for device drivers for various
> +	  Uniwill X86 platforms, including many OEM laptops originally
> +	  manufactured by Uniwill.
> +	  This option alone does not add any kernel code.
> +
> +	  If you say N, all options in this submenu will be skipped and disabled.
> +
> +if X86_PLATFORM_DRIVERS_UNIWILL
> +
> +config UNIWILL_WMI
> +	tristate "Uniwill WMI Event Driver"
> +	default m
> +	depends on ACPI_WMI
> +	depends on INPUT
> +	select INPUT_SPARSEKMAP
> +	help
> +	  This driver adds support for various hotkey events on Uniwill laptops,
> +	  like rfkill and other special buttons. It also supports many OEM laptops
> +	  originally manufactured by Uniwill.
> +
> +	  If you have such a laptop, say Y or M here.
> +
> +endif
> diff --git a/drivers/platform/x86/uniwill/Makefile b/drivers/platform/x86/uniwill/Makefile
> new file mode 100644
> index 000000000000..a5a300be63f3
> --- /dev/null
> +++ b/drivers/platform/x86/uniwill/Makefile
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: GPL-2.0-or-later
> +#
> +# Makefile for linux/drivers/platform/x86/uniwill
> +# Uniwill X86 Platform Specific Drivers
> +#
> +
> +obj-$(CONFIG_UNIWILL_WMI)	+= uniwill-wmi.o
> diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c
> new file mode 100644
> index 000000000000..b95a0d68ce6a
> --- /dev/null
> +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c
> @@ -0,0 +1,177 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Linux hotkey driver for Uniwill notebooks.
> + *
> + * Copyright (C) 2025 Armin Wolf <W_Armin@....de>
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/export.h>
> +#include <linux/input.h>
> +#include <linux/input/sparse-keymap.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/notifier.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/wmi.h>
> +
> +#include "uniwill-wmi.h"
> +
> +#define DRIVER_NAME		"uniwill-wmi"
> +#define UNIWILL_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
> +
> +struct uniwill_wmi_data {
> +	struct mutex input_lock;	/* Protects input sequence during notify */
> +	struct input_dev *input_device;
> +};
> +
> +static BLOCKING_NOTIFIER_HEAD(uniwill_wmi_chain_head);
> +
> +static const struct key_entry uniwill_wmi_keymap[] = {
> +	/* Reported via keyboard controller */
> +	{ KE_IGNORE,	UNIWILL_OSD_CAPSLOCK,			{ KEY_CAPSLOCK }},
> +	{ KE_IGNORE,	UNIWILL_OSD_NUMLOCK,			{ KEY_NUMLOCK }},
> +
> +	/* Reported when the user locks/unlocks the super key */
> +	{ KE_IGNORE,	UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE,	{ KEY_UNKNOWN }},
> +	{ KE_IGNORE,	UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE,	{ KEY_UNKNOWN }},
> +
> +	/* Reported in manual mode when toggling the airplane mode status */
> +	{ KE_KEY,	UNIWILL_OSD_RFKILL,			{ KEY_RFKILL }},
> +
> +	/* Reported when user wants to cycle the platform profile */
> +	{ KE_IGNORE,	UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE,	{ KEY_UNKNOWN }},
> +
> +	/* Reported when the user wants to toggle the microphone mute status */
> +	{ KE_KEY,	UNIWILL_OSD_MIC_MUTE,			{ KEY_MICMUTE }},
> +
> +	/* Reported when the user locks/unlocks the Fn key */
> +	{ KE_IGNORE,	UNIWILL_OSD_FN_LOCK,			{ KEY_FN_ESC }},
> +
> +	/* Reported when the user wants to toggle the brightness of the keyboard */
> +	{ KE_KEY,	UNIWILL_OSD_KBDILLUMTOGGLE,		{ KEY_KBDILLUMTOGGLE }},
> +
> +	/* FIXME: find out the exact meaning of those events */
> +	{ KE_IGNORE,	UNIWILL_OSD_BAT_CHARGE_FULL_24_H,	{ KEY_UNKNOWN }},
> +	{ KE_IGNORE,	UNIWILL_OSD_BAT_ERM_UPDATE,		{ KEY_UNKNOWN }},
> +
> +	/* Reported when the user wants to toggle the benchmark mode status */
> +	{ KE_IGNORE,	UNIWILL_OSD_BENCHMARK_MODE_TOGGLE,	{ KEY_UNKNOWN }},
> +
> +	{ KE_END }
> +};
> +
> +int uniwill_wmi_register_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_register(&uniwill_wmi_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(uniwill_wmi_register_notifier, "UNIWILL");
> +
> +int uniwill_wmi_unregister_notifier(struct notifier_block *nb)
> +{
> +	return blocking_notifier_chain_unregister(&uniwill_wmi_chain_head, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(uniwill_wmi_unregister_notifier, "UNIWILL");
> +
> +static void devm_uniwill_wmi_unregister_notifier(void *data)
> +{
> +	struct notifier_block *nb = data;
> +
> +	uniwill_wmi_unregister_notifier(nb);
> +}
> +
> +int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb)
> +{
> +	int ret;
> +
> +	ret = uniwill_wmi_register_notifier(nb);
> +	if (ret < 0)
> +		return ret;
> +
> +	return devm_add_action_or_reset(dev, devm_uniwill_wmi_unregister_notifier, nb);
> +}
> +EXPORT_SYMBOL_NS_GPL(devm_uniwill_wmi_register_notifier, "UNIWILL");
> +
> +static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj)
> +{
> +	struct uniwill_wmi_data *data = dev_get_drvdata(&wdev->dev);
> +	u32 value;
> +	int ret;
> +
> +	if (obj->type != ACPI_TYPE_INTEGER)
> +		return;
> +
> +	value = obj->integer.value;
> +
> +	dev_dbg(&wdev->dev, "Received WMI event %u\n", value);
> +
> +	ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL);
> +	if (ret == NOTIFY_BAD)
> +		return;
> +
> +	mutex_lock(&data->input_lock);
> +	sparse_keymap_report_event(data->input_device, value, 1, true);
> +	mutex_unlock(&data->input_lock);
> +}
> +
> +static int uniwill_wmi_probe(struct wmi_device *wdev, const void *context)
> +{
> +	struct uniwill_wmi_data *data;
> +	int ret;

Hmm, if someone manually modprobe-s this because they think it will
help it will still load despite the generic GUID thing.

I think this should the same DMI allow-list as the -laptop driver?

Or maybe just export the driver as a symbol and have -laptop do
the actual registering of the driver ? Then if e.g. a force module
option gets added to the -laptop driver that will not need to
be duplicated here ...

Regards,

Hans



> +
> +	data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	ret = devm_mutex_init(&wdev->dev, &data->input_lock);
> +	if (ret < 0)
> +		return ret;
> +
> +	dev_set_drvdata(&wdev->dev, data);
> +
> +	data->input_device = devm_input_allocate_device(&wdev->dev);
> +	if (!data->input_device)
> +		return -ENOMEM;
> +
> +	ret = sparse_keymap_setup(data->input_device, uniwill_wmi_keymap, NULL);
> +	if (ret < 0)
> +		return ret;
> +
> +	data->input_device->name = "Uniwill WMI hotkeys";
> +	data->input_device->phys = "wmi/input0";
> +	data->input_device->id.bustype = BUS_HOST;
> +
> +	return input_register_device(data->input_device);
> +}
> +
> +/*
> + * 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.
> + */
> +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,
> +	.probe = uniwill_wmi_probe,
> +	.notify = uniwill_wmi_notify,
> +	.no_singleton = true,
> +};
> +module_wmi_driver(uniwill_wmi_driver);
> +
> +MODULE_AUTHOR("Armin Wolf <W_Armin@....de>");
> +MODULE_DESCRIPTION("Uniwill notebook hotkey driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.h b/drivers/platform/x86/uniwill/uniwill-wmi.h
> new file mode 100644
> index 000000000000..41662ece0675
> --- /dev/null
> +++ b/drivers/platform/x86/uniwill/uniwill-wmi.h
> @@ -0,0 +1,122 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Linux hotkey driver for Uniwill notebooks.
> + *
> + * Copyright (C) 2025 Armin Wolf <W_Armin@....de>
> + */
> +
> +#ifndef UNIWILL_WMI_H
> +#define UNIWILL_WMI_H
> +
> +#define UNIWILL_OSD_CAPSLOCK			0x01
> +#define UNIWILL_OSD_NUMLOCK			0x02
> +#define UNIWILL_OSD_SCROLLLOCK			0x03
> +
> +#define UNIWILL_OSD_TOUCHPAD_ON			0x04
> +#define UNIWILL_OSD_TOUCHPAD_OFF		0x05
> +
> +#define UNIWILL_OSD_SILENT_MODE_ON		0x06
> +#define UNIWILL_OSD_SILENT_MODE_OFF		0x07
> +
> +#define UNIWILL_OSD_WLAN_ON			0x08
> +#define UNIWILL_OSD_WLAN_OFF			0x09
> +
> +#define UNIWILL_OSD_WIMAX_ON			0x0A
> +#define UNIWILL_OSD_WIMAX_OFF			0x0B
> +
> +#define UNIWILL_OSD_BLUETOOTH_ON		0x0C
> +#define UNIWILL_KEY_BLUETOOTH_OFF		0x0D
> +
> +#define UNIWILL_OSD_RF_ON			0x0E
> +#define UNIWILL_OSD_RF_OFF			0x0F
> +
> +#define UNIWILL_OSD_3G_ON			0x10
> +#define UNIWILL_OSD_3G_OFF			0x11
> +
> +#define UNIWILL_OSD_WEBCAM_ON			0x12
> +#define UNIWILL_OSD_WEBCAM_OFF			0x13
> +
> +#define UNIWILL_OSD_BRIGHTNESSUP		0x14
> +#define UNIWILL_OSD_BRIGHTNESSDOWN		0x15
> +
> +#define UNIWILL_OSD_RADIOON			0x1A
> +#define UNIWILL_OSD_RADIOOFF			0x1B
> +
> +#define UNIWILL_OSD_POWERSAVE_ON		0x31
> +#define UNIWILL_OSD_POWERSAVE_OFF		0x32
> +
> +#define UNIWILL_OSD_MENU			0x34
> +
> +#define UNIWILL_OSD_MUTE			0x35
> +#define UNIWILL_OSD_VOLUMEDOWN			0x36
> +#define UNIWILL_OSD_VOLUMEUP			0x37
> +
> +#define UNIWILL_OSD_MENU_2			0x38
> +
> +#define UNIWILL_OSD_LIGHTBAR_ON			0x39
> +#define UNIWILL_OSD_LIGHTBAR_OFF		0x3A
> +
> +#define UNIWILL_OSD_KB_LED_LEVEL0		0x3B
> +#define UNIWILL_OSD_KB_LED_LEVEL1		0x3C
> +#define UNIWILL_OSD_KB_LED_LEVEL2		0x3D
> +#define UNIWILL_OSD_KB_LED_LEVEL3		0x3E
> +#define UNIWILL_OSD_KB_LED_LEVEL4		0x3F
> +
> +#define UNIWILL_OSD_SUPER_KEY_LOCK_ENABLE	0x40
> +#define UNIWILL_OSD_SUPER_KEY_LOCK_DISABLE	0x41
> +
> +#define UNIWILL_OSD_MENU_JP			0x42
> +
> +#define UNIWILL_OSD_CAMERA_ON			0x90
> +#define UNIWILL_OSD_CAMERA_OFF			0x91
> +
> +#define UNIWILL_OSD_RFKILL			0xA4
> +
> +#define UNIWILL_OSD_SUPER_KEY_LOCK_CHANGED	0xA5
> +
> +#define UNIWILL_OSD_LIGHTBAR_STATE_CHANGED	0xA6
> +
> +#define UNIWILL_OSD_FAN_BOOST_STATE_CHANGED	0xA7
> +
> +#define UNIWILL_OSD_LCD_SW			0xA9
> +
> +#define UNIWILL_OSD_FAN_OVERTEMP		0xAA
> +
> +#define UNIWILL_OSD_DC_ADAPTER_CHANGED		0xAB
> +
> +#define UNIWILL_OSD_BAT_HP_OFF			0xAC
> +
> +#define UNIWILL_OSD_FAN_DOWN_TEMP		0xAD
> +
> +#define UNIWILL_OSD_BATTERY_ALERT		0xAE
> +
> +#define UNIWILL_OSD_TIMAP_HAIERLB_SW		0xAF
> +
> +#define UNIWILL_OSD_PERFORMANCE_MODE_TOGGLE	0xB0
> +
> +#define UNIWILL_OSD_KBDILLUMDOWN		0xB1
> +#define UNIWILL_OSD_KBDILLUMUP			0xB2
> +
> +#define UNIWILL_OSD_BACKLIGHT_LEVEL_CHANGE	0xB3
> +#define UNIWILL_OSD_BACKLIGHT_POWER_CHANGE	0xB4
> +
> +#define UNIWILL_OSD_MIC_MUTE			0xB7
> +
> +#define UNIWILL_OSD_FN_LOCK			0xB8
> +#define UNIWILL_OSD_KBDILLUMTOGGLE		0xB9
> +
> +#define UNIWILL_OSD_BAT_CHARGE_FULL_24_H	0xBE
> +
> +#define UNIWILL_OSD_BAT_ERM_UPDATE		0xBF
> +
> +#define UNIWILL_OSD_BENCHMARK_MODE_TOGGLE	0xC0
> +
> +#define UNIWILL_OSD_KBD_BACKLIGHT_CHANGED	0xF0
> +
> +struct notifier_block;
> +
> +int uniwill_wmi_register_notifier(struct notifier_block *nb);
> +int uniwill_wmi_unregister_notifier(struct notifier_block *nb);
> +int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block *nb);
> +
> +#endif /* UNIWILL_WMI_H */


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ