#include <linux/acpi.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/types.h>
#include <linux/wmi.h>
#include "wmi-events.h"
#include "wmi-gamezone.h"
#define THERMAL_MODE_EVENT_GUID "D320289E-8FEA-41E0-86F9-911D83151B5F"
#define LWMI_EVENT_DEVICE(guid, type) \
.guid_string = (guid), .context = &(enum lwmi_events_type) \
{ \
type \
}
static BLOCKING_NOTIFIER_HEAD(events_chain_head);
struct lwmi_events_priv {
struct wmi_device *wdev;
enum lwmi_events_type type;
};
int lwmi_events_register_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&events_chain_head, nb);
}
EXPORT_SYMBOL_NS_GPL(lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
int lwmi_events_unregister_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&events_chain_head, nb);
}
EXPORT_SYMBOL_NS_GPL(lwmi_events_unregister_notifier, "LENOVO_WMI_EVENTS");
static void devm_lwmi_events_unregister_notifier(void *data)
{
struct notifier_block *nb = data;
lwmi_events_unregister_notifier(nb);
}
int devm_lwmi_events_register_notifier(struct device *dev,
struct notifier_block *nb)
{
int ret;
ret = lwmi_events_register_notifier(nb);
if (ret < 0)
return ret;
return devm_add_action_or_reset(dev, devm_lwmi_events_unregister_notifier, nb);
}
EXPORT_SYMBOL_NS_GPL(devm_lwmi_events_register_notifier, "LENOVO_WMI_EVENTS");
static void lwmi_events_notify(struct wmi_device *wdev, union acpi_object *obj)
{
struct lwmi_events_priv *priv = dev_get_drvdata(&wdev->dev);
int sel_prof;
int ret;
switch (priv->type) {
case LWMI_EVENT_THERMAL_MODE:
if (obj->type != ACPI_TYPE_INTEGER)
return;
sel_prof = obj->integer.value;
switch (sel_prof) {
case LWMI_GZ_THERMAL_MODE_QUIET:
case LWMI_GZ_THERMAL_MODE_BALANCED:
case LWMI_GZ_THERMAL_MODE_PERFORMANCE:
case LWMI_GZ_THERMAL_MODE_EXTREME:
case LWMI_GZ_THERMAL_MODE_CUSTOM:
ret = blocking_notifier_call_chain(&events_chain_head,
LWMI_EVENT_THERMAL_MODE,
&sel_prof);
if (ret == NOTIFY_BAD)
dev_err(&wdev->dev,
"Failed to send notification to call chain for WMI Events\n");
return;
default:
dev_err(&wdev->dev, "Got invalid thermal mode: %x",
sel_prof);
return;
}
break;
default:
return;
}
}
static int lwmi_events_probe(struct wmi_device *wdev, const void *context)
{
struct lwmi_events_priv *priv;
if (!context)
return -EINVAL;
priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->wdev = wdev;
priv->type = *(enum lwmi_events_type *)context;
dev_set_drvdata(&wdev->dev, priv);
return 0;
}
static const struct wmi_device_id lwmi_events_id_table[] = {
{ LWMI_EVENT_DEVICE(THERMAL_MODE_EVENT_GUID, LWMI_EVENT_THERMAL_MODE) },
{}
};
static struct wmi_driver lwmi_events_driver = {
.driver = {
.name = "lenovo_wmi_events",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = lwmi_events_id_table,
.probe = lwmi_events_probe,
.notify = lwmi_events_notify,
.no_singleton = true,
};
module_wmi_driver(lwmi_events_driver);
MODULE_DEVICE_TABLE(wmi, lwmi_events_id_table);
MODULE_AUTHOR("Derek J. Clark <derekjohn.clark@gmail.com>");
MODULE_DESCRIPTION("Lenovo WMI Events Driver");
MODULE_LICENSE("GPL");