mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 01:53:29 +02:00
power: reset: reboot-mode: Expose sysfs for registered reboot_modes
Currently, there is no standardized mechanism for userspace to discover supported reboot modes on a platform. This limits userspace scripts, to rely on hardcoded assumptions about the available reboot-modes. Create a class 'reboot-mode' and a device under it. Use the name of the registering driver as device name. Expose a sysfs interface under this device to show available reboot mode arguments. This results in the creation of: /sys/class/reboot-mode/<driver>/reboot_modes This read-only sysfs file will exposes the supported reboot mode arguments provided by the registering driver, enabling userspace to query the list of arguments. Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com> Link: https://patch.msgid.link/20260224-next-15nov_expose_sysfs-v24-2-4ee5b49d5a06@oss.qualcomm.com Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
This commit is contained in:
parent
d3da03025e
commit
cfaf0a9078
|
|
@ -4,12 +4,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/list.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
#include <linux/reboot.h>
|
#include <linux/reboot.h>
|
||||||
#include <linux/reboot-mode.h>
|
#include <linux/reboot-mode.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
|
||||||
#define PREFIX "mode-"
|
#define PREFIX "mode-"
|
||||||
|
|
||||||
|
|
@ -19,6 +23,54 @@ struct mode_info {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct reboot_mode_sysfs_data {
|
||||||
|
struct device *reboot_mode_device;
|
||||||
|
struct list_head head;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void reboot_mode_release_list(struct reboot_mode_sysfs_data *priv)
|
||||||
|
{
|
||||||
|
struct mode_info *info;
|
||||||
|
struct mode_info *next;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(info, next, &priv->head, list) {
|
||||||
|
list_del(&info->list);
|
||||||
|
kfree_const(info->mode);
|
||||||
|
kfree(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t reboot_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
struct reboot_mode_sysfs_data *priv;
|
||||||
|
struct mode_info *sysfs_info;
|
||||||
|
ssize_t size = 0;
|
||||||
|
|
||||||
|
priv = dev_get_drvdata(dev);
|
||||||
|
if (!priv)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
list_for_each_entry(sysfs_info, &priv->head, list)
|
||||||
|
size += sysfs_emit_at(buf, size, "%s ", sysfs_info->mode);
|
||||||
|
|
||||||
|
if (!size)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
return size + sysfs_emit_at(buf, size - 1, "\n");
|
||||||
|
}
|
||||||
|
static DEVICE_ATTR_RO(reboot_modes);
|
||||||
|
|
||||||
|
static struct attribute *reboot_mode_attrs[] = {
|
||||||
|
&dev_attr_reboot_modes.attr,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
ATTRIBUTE_GROUPS(reboot_mode);
|
||||||
|
|
||||||
|
static const struct class reboot_mode_class = {
|
||||||
|
.name = "reboot-mode",
|
||||||
|
.dev_groups = reboot_mode_groups,
|
||||||
|
};
|
||||||
|
|
||||||
static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
|
static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
|
||||||
const char *cmd)
|
const char *cmd)
|
||||||
{
|
{
|
||||||
|
|
@ -62,6 +114,51 @@ static int reboot_mode_notify(struct notifier_block *this,
|
||||||
return NOTIFY_DONE;
|
return NOTIFY_DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int reboot_mode_create_device(struct reboot_mode_driver *reboot)
|
||||||
|
{
|
||||||
|
struct reboot_mode_sysfs_data *priv;
|
||||||
|
struct mode_info *sysfs_info;
|
||||||
|
struct mode_info *info;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
priv = kzalloc_obj(*priv, GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&priv->head);
|
||||||
|
|
||||||
|
list_for_each_entry(info, &reboot->head, list) {
|
||||||
|
sysfs_info = kzalloc_obj(*sysfs_info, GFP_KERNEL);
|
||||||
|
if (!sysfs_info) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
sysfs_info->mode = kstrdup_const(info->mode, GFP_KERNEL);
|
||||||
|
if (!sysfs_info->mode) {
|
||||||
|
kfree(sysfs_info);
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&sysfs_info->list, &priv->head);
|
||||||
|
}
|
||||||
|
|
||||||
|
priv->reboot_mode_device = device_create(&reboot_mode_class, NULL, 0,
|
||||||
|
(void *)priv, reboot->dev->driver->name);
|
||||||
|
if (IS_ERR(priv->reboot_mode_device)) {
|
||||||
|
ret = PTR_ERR(priv->reboot_mode_device);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
reboot_mode_release_list(priv);
|
||||||
|
kfree(priv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reboot_mode_register - register a reboot mode driver
|
* reboot_mode_register - register a reboot mode driver
|
||||||
* @reboot: reboot mode driver
|
* @reboot: reboot mode driver
|
||||||
|
|
@ -113,16 +210,49 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
|
||||||
reboot->reboot_notifier.notifier_call = reboot_mode_notify;
|
reboot->reboot_notifier.notifier_call = reboot_mode_notify;
|
||||||
register_reboot_notifier(&reboot->reboot_notifier);
|
register_reboot_notifier(&reboot->reboot_notifier);
|
||||||
|
|
||||||
|
ret = reboot_mode_create_device(reboot);
|
||||||
|
if (ret)
|
||||||
|
goto error;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
list_for_each_entry(info, &reboot->head, list)
|
reboot_mode_unregister(reboot);
|
||||||
kfree_const(info->mode);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(reboot_mode_register);
|
EXPORT_SYMBOL_GPL(reboot_mode_register);
|
||||||
|
|
||||||
|
static int reboot_mode_match_by_name(struct device *dev, const void *data)
|
||||||
|
{
|
||||||
|
const char *name = data;
|
||||||
|
|
||||||
|
if (!dev || !data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return dev_name(dev) && strcmp(dev_name(dev), name) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void reboot_mode_unregister_device(struct reboot_mode_driver *reboot)
|
||||||
|
{
|
||||||
|
struct reboot_mode_sysfs_data *priv;
|
||||||
|
struct device *reboot_mode_device;
|
||||||
|
|
||||||
|
reboot_mode_device = class_find_device(&reboot_mode_class, NULL, reboot->dev->driver->name,
|
||||||
|
reboot_mode_match_by_name);
|
||||||
|
|
||||||
|
if (!reboot_mode_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
priv = dev_get_drvdata(reboot_mode_device);
|
||||||
|
device_unregister(reboot_mode_device);
|
||||||
|
|
||||||
|
if (!priv)
|
||||||
|
return;
|
||||||
|
|
||||||
|
reboot_mode_release_list(priv);
|
||||||
|
kfree(priv);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reboot_mode_unregister - unregister a reboot mode driver
|
* reboot_mode_unregister - unregister a reboot mode driver
|
||||||
* @reboot: reboot mode driver
|
* @reboot: reboot mode driver
|
||||||
|
|
@ -132,6 +262,7 @@ int reboot_mode_unregister(struct reboot_mode_driver *reboot)
|
||||||
struct mode_info *info;
|
struct mode_info *info;
|
||||||
|
|
||||||
unregister_reboot_notifier(&reboot->reboot_notifier);
|
unregister_reboot_notifier(&reboot->reboot_notifier);
|
||||||
|
reboot_mode_unregister_device(reboot);
|
||||||
|
|
||||||
list_for_each_entry(info, &reboot->head, list)
|
list_for_each_entry(info, &reboot->head, list)
|
||||||
kfree_const(info->mode);
|
kfree_const(info->mode);
|
||||||
|
|
@ -199,6 +330,19 @@ void devm_reboot_mode_unregister(struct device *dev,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
|
EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
|
||||||
|
|
||||||
|
static int __init reboot_mode_init(void)
|
||||||
|
{
|
||||||
|
return class_register(&reboot_mode_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit reboot_mode_exit(void)
|
||||||
|
{
|
||||||
|
class_unregister(&reboot_mode_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
subsys_initcall(reboot_mode_init);
|
||||||
|
module_exit(reboot_mode_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
|
MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
|
||||||
MODULE_DESCRIPTION("System reboot mode core library");
|
MODULE_DESCRIPTION("System reboot mode core library");
|
||||||
MODULE_LICENSE("GPL v2");
|
MODULE_LICENSE("GPL v2");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user