// SPDX-License-Identifier: GPL-2.0-or-later /* * Lenovo Other Mode WMI interface driver. * * This driver uses the fw_attributes class to expose the various WMI functions * provided by the "Other Mode" WMI interface. This enables CPU and GPU power * limit as well as various other attributes for devices that fall under the * "Gaming Series" of Lenovo laptop devices. Each attribute exposed by the * "Other Mode" interface has a corresponding Capability Data struct that * allows the driver to probe details about the attribute such as if it is * supported by the hardware, the default_value, max_value, min_value, and step * increment. * * These attributes typically don't fit anywhere else in the sysfs and are set * in Windows using one of Lenovo's multiple user applications. * * Copyright (C) 2025 Derek J. Clark <derekjohn.clark@gmail.com>
*/
/** * lwmi_om_register_notifier() - Add a notifier to the blocking notifier chain * @nb: The notifier_block struct to register * * Call blocking_notifier_chain_register to register the notifier block to the * lenovo-wmi-other driver notifier chain. * * Return: 0 on success, %-EEXIST on error.
*/ int lwmi_om_register_notifier(struct notifier_block *nb)
{ return blocking_notifier_chain_register(&om_chain_head, nb);
}
EXPORT_SYMBOL_NS_GPL(lwmi_om_register_notifier, "LENOVO_WMI_OTHER");
/** * lwmi_om_unregister_notifier() - Remove a notifier from the blocking notifier * chain. * @nb: The notifier_block struct to register * * Call blocking_notifier_chain_unregister to unregister the notifier block from the * lenovo-wmi-other driver notifier chain. * * Return: 0 on success, %-ENOENT on error.
*/ int lwmi_om_unregister_notifier(struct notifier_block *nb)
{ return blocking_notifier_chain_unregister(&om_chain_head, nb);
}
EXPORT_SYMBOL_NS_GPL(lwmi_om_unregister_notifier, "LENOVO_WMI_OTHER");
/** * devm_lwmi_om_unregister_notifier() - Remove a notifier from the blocking * notifier chain. * @data: Void pointer to the notifier_block struct to register. * * Call lwmi_om_unregister_notifier to unregister the notifier block from the * lenovo-wmi-other driver notifier chain. * * Return: 0 on success, %-ENOENT on error.
*/ staticvoid devm_lwmi_om_unregister_notifier(void *data)
{ struct notifier_block *nb = data;
lwmi_om_unregister_notifier(nb);
}
/** * devm_lwmi_om_register_notifier() - Add a notifier to the blocking notifier * chain. * @dev: The parent device of the notifier_block struct. * @nb: The notifier_block struct to register * * Call lwmi_om_register_notifier to register the notifier block to the * lenovo-wmi-other driver notifier chain. Then add devm_lwmi_om_unregister_notifier * as a device managed action to automatically unregister the notifier block * upon parent device removal. * * Return: 0 on success, or an error code.
*/ int devm_lwmi_om_register_notifier(struct device *dev, struct notifier_block *nb)
{ int ret;
ret = lwmi_om_register_notifier(nb); if (ret < 0) return ret;
/** * lwmi_om_notifier_call() - Call functions for the notifier call chain. * @mode: Pointer to a thermal mode enum to retrieve the data from. * * Call blocking_notifier_call_chain to retrieve the thermal mode from the * lenovo-wmi-gamezone driver. * * Return: 0 on success, or an error code.
*/ staticint lwmi_om_notifier_call(enum thermal_mode *mode)
{ int ret;
ret = blocking_notifier_call_chain(&om_chain_head,
LWMI_GZ_GET_THERMAL_MODE, &mode); if ((ret & ~NOTIFY_STOP_MASK) != NOTIFY_OK) return -EINVAL;
return 0;
}
/* Attribute Methods */
/** * int_type_show() - Emit the data type for an integer attribute * @kobj: Pointer to the driver object. * @kattr: Pointer to the attribute calling this function. * @buf: The buffer to write to. * * Return: Number of characters written to buf.
*/ static ssize_t int_type_show(struct kobject *kobj, struct kobj_attribute *kattr, char *buf)
{ return sysfs_emit(buf, "integer\n");
}
/** * attr_capdata01_show() - Get the value of the specified attribute property * * @kobj: Pointer to the driver object. * @kattr: Pointer to the attribute calling this function. * @buf: The buffer to write to. * @tunable_attr: The attribute to be read. * @prop: The property of this attribute to be read. * * Retrieves the given property from the capability data 01 struct for the * specified attribute's "custom" thermal mode. This function is intended * to be generic so it can be called from any integer attributes "_show" * function. * * If the WMI is success the sysfs attribute is notified. * * Return: Either number of characters written to buf, or an error code.
*/ static ssize_t attr_capdata01_show(struct kobject *kobj, struct kobj_attribute *kattr, char *buf, struct tunable_attr_01 *tunable_attr, enum attribute_property prop)
{ struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); struct capdata01 capdata;
u32 attribute_id; int value, ret;
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); if (ret) return ret;
switch (prop) { case DEFAULT_VAL:
value = capdata.default_value; break; case MAX_VAL:
value = capdata.max_value; break; case MIN_VAL:
value = capdata.min_value; break; case STEP_VAL:
value = capdata.step; break; default: return -EINVAL;
}
return sysfs_emit(buf, "%d\n", value);
}
/** * attr_current_value_store() - Set the current value of the given attribute * @kobj: Pointer to the driver object. * @kattr: Pointer to the attribute calling this function. * @buf: The buffer to read from, this is parsed to `int` type. * @count: Required by sysfs attribute macros, pass in from the callee attr. * @tunable_attr: The attribute to be stored. * * Sets the value of the given attribute when operating under the "custom" * smartfan profile. The current smartfan profile is retrieved from the * lenovo-wmi-gamezone driver and error is returned if the result is not * "custom". This function is intended to be generic so it can be called from * any integer attribute's "_store" function. The integer to be sent to the WMI * method is range checked and an error code is returned if out of range. * * If the value is valid and WMI is success, then the sysfs attribute is * notified. * * Return: Either count, or an error code.
*/ static ssize_t attr_current_value_store(struct kobject *kobj, struct kobj_attribute *kattr, constchar *buf, size_t count, struct tunable_attr_01 *tunable_attr)
{ struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); struct wmi_method_args_32 args; struct capdata01 capdata; enum thermal_mode mode;
u32 attribute_id;
u32 value; int ret;
ret = lwmi_om_notifier_call(&mode); if (ret) return ret;
if (mode != LWMI_GZ_THERMAL_MODE_CUSTOM) return -EBUSY;
ret = lwmi_cd01_get_data(priv->cd01_list, attribute_id, &capdata); if (ret) return ret;
ret = kstrtouint(buf, 10, &value); if (ret) return ret;
if (value < capdata.min_value || value > capdata.max_value) return -EINVAL;
args.arg0 = attribute_id;
args.arg1 = value;
ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET,
(unsignedchar *)&args, sizeof(args), NULL); if (ret) return ret;
return count;
};
/** * attr_current_value_show() - Get the current value of the given attribute * @kobj: Pointer to the driver object. * @kattr: Pointer to the attribute calling this function. * @buf: The buffer to write to. * @tunable_attr: The attribute to be read. * * Retrieves the value of the given attribute for the current smartfan profile. * The current smartfan profile is retrieved from the lenovo-wmi-gamezone driver. * This function is intended to be generic so it can be called from any integer * attribute's "_show" function. * * If the WMI is success the sysfs attribute is notified. * * Return: Either number of characters written to buf, or an error code.
*/ static ssize_t attr_current_value_show(struct kobject *kobj, struct kobj_attribute *kattr, char *buf, struct tunable_attr_01 *tunable_attr)
{ struct lwmi_om_priv *priv = dev_get_drvdata(tunable_attr->dev); struct wmi_method_args_32 args; enum thermal_mode mode;
u32 attribute_id; int retval; int ret;
ret = lwmi_om_notifier_call(&mode); if (ret) return ret;
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl", "Set the CPU sustained power limit");
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt", "Set the CPU slow package power tracking limit");
LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt", "Set the CPU fast package power tracking limit");
/** * lwmi_om_fw_attr_add() - Register all firmware_attributes_class members * @priv: The Other Mode driver data. * * Return: Either 0, or an error code.
*/ staticint lwmi_om_fw_attr_add(struct lwmi_om_priv *priv)
{ unsignedint i; int err;
priv->ida_id = ida_alloc(&lwmi_om_ida, GFP_KERNEL); if (priv->ida_id < 0) return priv->ida_id;
for (i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++) {
err = sysfs_create_group(&priv->fw_attr_kset->kobj,
cd01_attr_groups[i].attr_group); if (err) goto err_remove_groups;
/** * lwmi_om_fw_attr_remove() - Unregister all capability data attribute groups * @priv: the lenovo-wmi-other driver data.
*/ staticvoid lwmi_om_fw_attr_remove(struct lwmi_om_priv *priv)
{ for (unsignedint i = 0; i < ARRAY_SIZE(cd01_attr_groups) - 1; i++)
sysfs_remove_group(&priv->fw_attr_kset->kobj,
cd01_attr_groups[i].attr_group);
/** * lwmi_om_master_bind() - Bind all components of the other mode driver * @dev: The lenovo-wmi-other driver basic device. * * Call component_bind_all to bind the lenovo-wmi-capdata01 driver to the * lenovo-wmi-other master driver. On success, assign the capability data 01 * list pointer to the driver data struct for later access. This pointer * is only valid while the capdata01 interface exists. Finally, register all * firmware attribute groups. * * Return: 0 on success, or an error code.
*/ staticint lwmi_om_master_bind(struct device *dev)
{ struct lwmi_om_priv *priv = dev_get_drvdata(dev); struct cd01_list *tmp_list; int ret;
ret = component_bind_all(dev, &tmp_list); if (ret) return ret;
priv->cd01_list = tmp_list; if (!priv->cd01_list) return -ENODEV;
return lwmi_om_fw_attr_add(priv);
}
/** * lwmi_om_master_unbind() - Unbind all components of the other mode driver * @dev: The lenovo-wmi-other driver basic device * * Unregister all capability data attribute groups. Then call * component_unbind_all to unbind the lenovo-wmi-capdata01 driver from the * lenovo-wmi-other master driver. Finally, free the IDA for this device.
*/ staticvoid lwmi_om_master_unbind(struct device *dev)
{ struct lwmi_om_priv *priv = dev_get_drvdata(dev);
MODULE_IMPORT_NS("LENOVO_WMI_CD01");
MODULE_IMPORT_NS("LENOVO_WMI_HELPERS");
MODULE_DEVICE_TABLE(wmi, lwmi_other_id_table);
MODULE_AUTHOR("Derek J. Clark ");
MODULE_DESCRIPTION("Lenovo Other Mode WMI Driver");
MODULE_LICENSE("GPL");
Messung V0.5
¤ Dauer der Verarbeitung: 0.1 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.