File Index
Files and directories
Sign in
FILE

wmi-events.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/lenovo/wmi-events.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Lenovo WMI Events driver. Lenovo WMI interfaces provide various
  4 * hardware triggered events that many drivers need to have propagated.
  5 * This driver provides a uniform entrypoint for these events so that
  6 * any driver that needs to respond to these events can subscribe to a
  7 * notifier chain.
  8 *
  9 * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
 10 */
 11
 12#include <linux/acpi.h>
 13#include <linux/export.h>
 14#include <linux/module.h>
 15#include <linux/notifier.h>
 16#include <linux/types.h>
 17#include <linux/wmi.h>
 18
 19#include "wmi-events.h"
 20#include "wmi-gamezone.h"
 21
 22#define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
 23
 24#define LWMI_EVENT_DEVICE(guid, type)                        \
 25	.guid_string = (guid), .context = &(enum lwmi_events_type) \
 26	{                                                          \
 27		type                                               \
 28	}
 29
 30static BLOCKING_NOTIFIER_HEAD(events_chain_head);
 31
 32struct lwmi_events_priv {
 33	struct wmi_device *wdev;
 34	enum lwmi_events_type type;
 35};
 36
 37/**
 38 * lwmi_events_register_notifier() - Add a notifier to the notifier chain.
 39 * @nb: The notifier_block struct to register
 40 *
 41 * Call blocking_notifier_chain_register to register the notifier block to the
 42 * lenovo-wmi-events driver blocking notifier chain.
 43 *
 44 * Return: 0 on success, %-EEXIST on error.
 45 */
 46int lwmi_events_register_notifier(struct notifier_block *nb)
 47{
 48	return blocking_notifier_chain_register(&events_chain_head, nb);
 49}
 50EXPORT_SYMBOL_NS_GPL(lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
 51
 52/**
 53 * lwmi_events_unregister_notifier() - Remove a notifier from the notifier
 54 * chain.
 55 * @nb: The notifier_block struct to unregister
 56 *
 57 * Call blocking_notifier_chain_unregister to unregister the notifier block
 58 * from the lenovo-wmi-events driver blocking notifier chain.
 59 *
 60 * Return: 0 on success, %-ENOENT on error.
 61 */
 62int lwmi_events_unregister_notifier(struct notifier_block *nb)
 63{
 64	return blocking_notifier_chain_unregister(&events_chain_head, nb);
 65}
 66EXPORT_SYMBOL_NS_GPL(lwmi_events_unregister_notifier, "LENOVO_WMI_EVENTS");
 67
 68/**
 69 * devm_lwmi_events_unregister_notifier() - Remove a notifier from the notifier
 70 * chain.
 71 * @data: Void pointer to the notifier_block struct to unregister.
 72 *
 73 * Call lwmi_events_unregister_notifier to unregister the notifier block from
 74 * the lenovo-wmi-events driver blocking notifier chain.
 75 *
 76 * Return: 0 on success, %-ENOENT on error.
 77 */
 78static void devm_lwmi_events_unregister_notifier(void *data)
 79{
 80	struct notifier_block *nb = data;
 81
 82	lwmi_events_unregister_notifier(nb);
 83}
 84
 85/**
 86 * devm_lwmi_events_register_notifier() - Add a notifier to the notifier chain.
 87 * @dev: The parent device of the notifier_block struct.
 88 * @nb: The notifier_block struct to register
 89 *
 90 * Call lwmi_events_register_notifier to register the notifier block to the
 91 * lenovo-wmi-events driver blocking notifier chain. Then add, as a device
 92 * managed action, unregister_notifier to automatically unregister the
 93 * notifier block upon its parent device removal.
 94 *
 95 * Return: 0 on success, or an error code.
 96 */
 97int devm_lwmi_events_register_notifier(struct device *dev,
 98				       struct notifier_block *nb)
 99{
100	int ret;
101
102	ret = lwmi_events_register_notifier(nb);
103	if (ret < 0)
104		return ret;
105
106	return devm_add_action_or_reset(dev, devm_lwmi_events_unregister_notifier, nb);
107}
108EXPORT_SYMBOL_NS_GPL(devm_lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
109
110/**
111 * lwmi_events_notify() - Call functions for the notifier call chain.
112 * @wdev: The parent WMI device of the driver.
113 * @obj: ACPI object passed by the registered WMI Event.
114 *
115 * Validate WMI event data and notify all registered drivers of the event and
116 * its output.
117 *
118 * Return: 0 on success, or an error code.
119 */
120static void lwmi_events_notify(struct wmi_device *wdev, union acpi_object *obj)
121{
122	struct lwmi_events_priv *priv = dev_get_drvdata(&wdev->dev);
123	int sel_prof;
124	int ret;
125
126	switch (priv->type) {
127	case LWMI_EVENT_THERMAL_MODE:
128		if (obj->type != ACPI_TYPE_INTEGER)
129			return;
130
131		sel_prof = obj->integer.value;
132
133		switch (sel_prof) {
134		case LWMI_GZ_THERMAL_MODE_QUIET:
135		case LWMI_GZ_THERMAL_MODE_BALANCED:
136		case LWMI_GZ_THERMAL_MODE_PERFORMANCE:
137		case LWMI_GZ_THERMAL_MODE_EXTREME:
138		case LWMI_GZ_THERMAL_MODE_CUSTOM:
139			ret = blocking_notifier_call_chain(&events_chain_head,
140							   LWMI_EVENT_THERMAL_MODE,
141							   &sel_prof);
142			if (ret == NOTIFY_BAD)
143				dev_err(&wdev->dev,
144					"Failed to send notification to call chain for WMI Events\n");
145			return;
146		default:
147			dev_err(&wdev->dev, "Got invalid thermal mode: %x",
148				sel_prof);
149			return;
150		}
151		break;
152	default:
153		return;
154	}
155}
156
157static int lwmi_events_probe(struct wmi_device *wdev, const void *context)
158{
159	struct lwmi_events_priv *priv;
160
161	if (!context)
162		return -EINVAL;
163
164	priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
165	if (!priv)
166		return -ENOMEM;
167
168	priv->wdev = wdev;
169	priv->type = *(enum lwmi_events_type *)context;
170	dev_set_drvdata(&wdev->dev, priv);
171
172	return 0;
173}
174
175static const struct wmi_device_id lwmi_events_id_table[] = {
176	{ LWMI_EVENT_DEVICE(THERMAL_MODE_EVENT_GUID, LWMI_EVENT_THERMAL_MODE) },
177	{}
178};
179
180static struct wmi_driver lwmi_events_driver = {
181	.driver = {
182		.name = "lenovo_wmi_events",
183		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
184	},
185	.id_table = lwmi_events_id_table,
186	.probe = lwmi_events_probe,
187	.notify = lwmi_events_notify,
188	.no_singleton = true,
189};
190
191module_wmi_driver(lwmi_events_driver);
192
193MODULE_DEVICE_TABLE(wmi, lwmi_events_id_table);
194MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
195MODULE_DESCRIPTION("Lenovo WMI Events Driver");
196MODULE_LICENSE("GPL");
~/ Read-only file preview.