]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: sysfs: export the GPIO directory locally in the gpiochip<id> directory
authorBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Fri, 4 Jul 2025 12:58:55 +0000 (14:58 +0200)
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Wed, 16 Jul 2025 08:27:08 +0000 (10:27 +0200)
As a way to allow the user-space to stop referring to GPIOs by their
global numbers, introduce a parallel group of line attributes for
exported GPIO that live inside the GPIO chip class device and are
referred to by their HW offset within their parent chip.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-8-9289d8758243@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Documentation/ABI/obsolete/sysfs-gpio
drivers/gpio/gpiolib-sysfs.c

index ff694708a3bef787afa42dedf94faf209c44dbf0..0d3f12c4dcbde4f93da33707cd36e9acc0ee2fbf 100644 (file)
@@ -27,6 +27,9 @@ Description:
            /base ... (r/o) same as N
            /label ... (r/o) descriptive chip name
            /ngpio ... (r/o) number of GPIOs; numbered N to N + (ngpio - 1)
+           /gpio<OFFSET>
+               /value ... always readable, writes fail for input GPIOs
+               /direction ... r/w as: in, out (default low); write: high, low
        /chipX ... for each gpiochip; #X is the gpio device ID
            /export ... asks the kernel to export a GPIO at HW offset X to userspace
            /unexport ... to return a GPIO at HW offset X to the kernel
index ccc293a4cc5d51294703959317061af55fb0dab0..563e38456c33cd3a6e8674485105ef45ce8f5095 100644 (file)
@@ -41,6 +41,13 @@ enum {
        GPIO_SYSFS_LINE_CLASS_ATTR_SIZE,
 };
 
+enum {
+       GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION = 0,
+       GPIO_SYSFS_LINE_CHIP_ATTR_VALUE,
+       GPIO_SYSFS_LINE_CHIP_ATTR_SENTINEL,
+       GPIO_SYSFS_LINE_CHIP_ATTR_SIZE,
+};
+
 struct gpiod_data {
        struct list_head list;
 
@@ -54,6 +61,7 @@ struct gpiod_data {
 
        bool direction_can_change;
 
+       struct kobject *parent;
        struct device_attribute dir_attr;
        struct device_attribute val_attr;
        struct device_attribute edge_attr;
@@ -62,6 +70,10 @@ struct gpiod_data {
        struct attribute *class_attrs[GPIO_SYSFS_LINE_CLASS_ATTR_SIZE];
        struct attribute_group class_attr_group;
        const struct attribute_group *class_attr_groups[2];
+
+       struct attribute *chip_attrs[GPIO_SYSFS_LINE_CHIP_ATTR_SIZE];
+       struct attribute_group chip_attr_group;
+       const struct attribute_group *chip_attr_groups[2];
 };
 
 struct gpiodev_data {
@@ -691,6 +703,7 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name,
  */
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 {
+       char *path __free(kfree) = NULL;
        struct gpiodev_data *gdev_data;
        struct gpiod_data *desc_data;
        struct gpio_device *gdev;
@@ -780,13 +793,46 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
        gdev_data = gdev_get_data(gdev);
        if (!gdev_data) {
                status = -ENODEV;
-               goto err_unregister_device;
+               goto err_put_dirent;
+       }
+
+       desc_data->chip_attr_group.name = kasprintf(GFP_KERNEL, "gpio%u",
+                                                   gpio_chip_hwgpio(desc));
+       if (!desc_data->chip_attr_group.name) {
+               status = -ENOMEM;
+               goto err_put_dirent;
+       }
+
+       attrs = desc_data->chip_attrs;
+       desc_data->chip_attr_group.is_visible = gpio_is_visible;
+       attrs[GPIO_SYSFS_LINE_CHIP_ATTR_DIRECTION] = &desc_data->dir_attr.attr;
+       attrs[GPIO_SYSFS_LINE_CHIP_ATTR_VALUE] = &desc_data->val_attr.attr;
+
+       desc_data->chip_attr_group.attrs = attrs;
+       desc_data->chip_attr_groups[0] = &desc_data->chip_attr_group;
+
+       desc_data->parent = &gdev_data->cdev_id->kobj;
+       status = sysfs_create_groups(desc_data->parent,
+                                    desc_data->chip_attr_groups);
+       if (status)
+               goto err_free_name;
+
+       path = kasprintf(GFP_KERNEL, "gpio%u/value", gpio_chip_hwgpio(desc));
+       if (!path) {
+               status = -ENOMEM;
+               goto err_remove_groups;
        }
 
        list_add(&desc_data->list, &gdev_data->exported_lines);
 
        return 0;
 
+err_remove_groups:
+       sysfs_remove_groups(desc_data->parent, desc_data->chip_attr_groups);
+err_free_name:
+       kfree(desc_data->chip_attr_group.name);
+err_put_dirent:
+       sysfs_put(desc_data->value_kn);
 err_unregister_device:
        device_unregister(desc_data->dev);
 err_free_data:
@@ -883,6 +929,9 @@ void gpiod_unexport(struct gpio_desc *desc)
                 */
                if (desc_data->irq_flags)
                        gpio_sysfs_free_irq(desc_data);
+
+               sysfs_remove_groups(desc_data->parent,
+                                   desc_data->chip_attr_groups);
        }
 
        mutex_destroy(&desc_data->mutex);