]> www.infradead.org Git - users/willy/xarray.git/commitdiff
power: supply: core: add UAPI to discover currently used extensions
authorThomas Weißschuh <linux@weissschuh.net>
Wed, 11 Dec 2024 19:57:58 +0000 (20:57 +0100)
committerSebastian Reichel <sebastian.reichel@collabora.com>
Sat, 14 Dec 2024 21:50:17 +0000 (22:50 +0100)
Userspace wants to now about the used power supply extensions,
for example to handle a device extended by a certain extension
differently or to discover information about the extending device.

Add a sysfs directory to the power supply device.
This directory contains links which are named after the used extension
and point to the device implementing that extension.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Link: https://lore.kernel.org/r/20241211-power-supply-extensions-v6-4-9d9dc3f3d387@weissschuh.net
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
Documentation/ABI/testing/sysfs-class-power
drivers/power/supply/cros_charge-control.c
drivers/power/supply/power_supply.h
drivers/power/supply/power_supply_core.c
drivers/power/supply/power_supply_sysfs.c
drivers/power/supply/test_power.c
include/linux/power_supply.h

index 74050dfb5fc01019b2d85926b83172edd88eb3b3..cb53dde20a801bc1f6888634d6986cba11e3d328 100644 (file)
@@ -813,3 +813,12 @@ Description:
 
                Access: Read
                Valid values: 1-31
+
+What:          /sys/class/power_supply/<supply_name>/extensions/<extension_name>
+Date:          March 2025
+Contact:       linux-pm@vger.kernel.org
+Description:
+               Reports the extensions registered to the power supply.
+               Each entry is a link to the device which registered the extension.
+
+               Access: Read
index fb4af232721dec1d4f0090f6616922848812b2a2..02d5bdbe2e8d45108dd8f2d3ab6a927b94864b9e 100644 (file)
@@ -31,6 +31,7 @@
  */
 
 struct cros_chctl_priv {
+       struct device *dev;
        struct cros_ec_device *cros_ec;
        struct acpi_battery_hook battery_hook;
        struct power_supply *hooked_battery;
@@ -202,6 +203,7 @@ static int cros_chctl_psy_prop_is_writeable(struct power_supply *psy,
        };                                                                      \
                                                                                \
        static const struct power_supply_ext _name = {                          \
+               .name                   = "cros-charge-control",                \
                .properties             = _name ## _props,                      \
                .num_properties         = ARRAY_SIZE(_name ## _props),          \
                .charge_behaviours      = EC_CHARGE_CONTROL_BEHAVIOURS,         \
@@ -233,7 +235,7 @@ static int cros_chctl_add_battery(struct power_supply *battery, struct acpi_batt
                return 0;
 
        priv->hooked_battery = battery;
-       return power_supply_register_extension(battery, priv->psy_ext, priv);
+       return power_supply_register_extension(battery, priv->psy_ext, priv->dev, priv);
 }
 
 static int cros_chctl_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook)
@@ -299,6 +301,7 @@ static int cros_chctl_probe(struct platform_device *pdev)
 
        dev_dbg(dev, "Command version: %u\n", (unsigned int)priv->cmd_version);
 
+       priv->dev = dev;
        priv->cros_ec = cros_ec;
 
        if (priv->cmd_version == 1)
index 531785516d2ac31f9a7f73a58e15e64cb81820ed..9ed749cd09369f0f13017847687509736b30aae8 100644 (file)
@@ -25,6 +25,7 @@ extern bool power_supply_ext_has_property(const struct power_supply_ext *ext,
 struct power_supply_ext_registration {
        struct list_head list_head;
        const struct power_supply_ext *ext;
+       struct device *dev;
        void *data;
 };
 
@@ -39,6 +40,7 @@ struct power_supply_ext_registration {
 
 extern void __init power_supply_init_attrs(void);
 extern int power_supply_uevent(const struct device *dev, struct kobj_uevent_env *env);
+extern const struct attribute_group power_supply_extension_group;
 extern const struct attribute_group *power_supply_attr_groups[];
 
 #else
index 29209c19ea670533d168f219797b656550523627..76ebaef403ed5cf0c00691dd9633bb12ce774fb1 100644 (file)
@@ -1358,17 +1358,21 @@ static int power_supply_update_sysfs_and_hwmon(struct power_supply *psy)
 }
 
 int power_supply_register_extension(struct power_supply *psy, const struct power_supply_ext *ext,
-                                   void *data)
+                                   struct device *dev, void *data)
 {
        struct power_supply_ext_registration *reg;
        size_t i;
        int ret;
 
-       if (!psy || !ext || !ext->properties || !ext->num_properties)
+       if (!psy || !dev || !ext || !ext->name || !ext->properties || !ext->num_properties)
                return -EINVAL;
 
        guard(rwsem_write)(&psy->extensions_sem);
 
+       power_supply_for_each_extension(reg, psy)
+               if (strcmp(ext->name, reg->ext->name) == 0)
+                       return -EEXIST;
+
        for (i = 0; i < ext->num_properties; i++)
                if (power_supply_has_property(psy, ext->properties[i]))
                        return -EEXIST;
@@ -1378,9 +1382,15 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
                return -ENOMEM;
 
        reg->ext = ext;
+       reg->dev = dev;
        reg->data = data;
        list_add(&reg->list_head, &psy->extensions);
 
+       ret = sysfs_add_link_to_group(&psy->dev.kobj, power_supply_extension_group.name,
+                                     &dev->kobj, ext->name);
+       if (ret)
+               goto sysfs_link_failed;
+
        ret = power_supply_update_sysfs_and_hwmon(psy);
        if (ret)
                goto sysfs_hwmon_failed;
@@ -1388,6 +1398,8 @@ int power_supply_register_extension(struct power_supply *psy, const struct power
        return 0;
 
 sysfs_hwmon_failed:
+       sysfs_remove_link_from_group(&psy->dev.kobj, power_supply_extension_group.name, ext->name);
+sysfs_link_failed:
        list_del(&reg->list_head);
        kfree(reg);
        return ret;
@@ -1403,6 +1415,9 @@ void power_supply_unregister_extension(struct power_supply *psy, const struct po
        power_supply_for_each_extension(reg, psy) {
                if (reg->ext == ext) {
                        list_del(&reg->list_head);
+                       sysfs_remove_link_from_group(&psy->dev.kobj,
+                                                    power_supply_extension_group.name,
+                                                    reg->ext->name);
                        kfree(reg);
                        power_supply_update_sysfs_and_hwmon(psy);
                        return;
index b8f73bc89b05c6e68891c4c5352910afd6f94616..261bf7bf6e22e3e9ac49074662220ed8db4aa207 100644 (file)
@@ -458,8 +458,18 @@ static const struct attribute_group power_supply_attr_group = {
        .is_visible = power_supply_attr_is_visible,
 };
 
+static struct attribute *power_supply_extension_attrs[] = {
+       NULL
+};
+
+const struct attribute_group power_supply_extension_group = {
+       .name = "extensions",
+       .attrs = power_supply_extension_attrs,
+};
+
 const struct attribute_group *power_supply_attr_groups[] = {
        &power_supply_attr_group,
+       &power_supply_extension_group,
        NULL
 };
 
index 66f9ef52e0f3e6e6e6bebcfd438c2acd421284ec..2a975a110f4859a77f7689369675f2008816d704 100644 (file)
@@ -293,6 +293,7 @@ static int test_power_battery_extproperty_is_writeable(struct power_supply *psy,
 }
 
 static const struct power_supply_ext test_power_battery_ext = {
+       .name                   = "test_power",
        .properties             = test_power_battery_extprops,
        .num_properties         = ARRAY_SIZE(test_power_battery_extprops),
        .get_property           = test_power_battery_extget_property,
@@ -307,7 +308,8 @@ static void test_power_configure_battery_extension(bool enable)
        psy = test_power_supplies[TEST_BATTERY];
 
        if (enable) {
-               if (power_supply_register_extension(psy, &test_power_battery_ext, NULL)) {
+               if (power_supply_register_extension(psy, &test_power_battery_ext, &psy->dev,
+                                                   NULL)) {
                        pr_err("registering battery extension failed\n");
                        return;
                }
index a877518cd963d10cdcb2c2fc3a71a50a6e6098b8..c3ce9f2b17d42650a648e64d6e4116aa8d96241a 100644 (file)
@@ -286,6 +286,7 @@ struct power_supply_desc {
 };
 
 struct power_supply_ext {
+       const char *const name;
        u8 charge_behaviours;
        const enum power_supply_property *properties;
        size_t num_properties;
@@ -911,6 +912,7 @@ extern int power_supply_powers(struct power_supply *psy, struct device *dev);
 extern int __must_check
 power_supply_register_extension(struct power_supply *psy,
                                const struct power_supply_ext *ext,
+                               struct device *dev,
                                void *data);
 extern void power_supply_unregister_extension(struct power_supply *psy,
                                              const struct power_supply_ext *ext);