diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index fc9c19fde3f1..e10d720ee0ad 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -32,6 +32,15 @@ struct kernfs_node; #define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \ GPIO_IRQF_TRIGGER_RISING) +enum { + GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION = 0, + GPIO_SYSFS_LINE_CLASS_ATTR_VALUE, + GPIO_SYSFS_LINE_CLASS_ATTR_EDGE, + GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW, + GPIO_SYSFS_LINE_CLASS_ATTR_SENTINEL, + GPIO_SYSFS_LINE_CLASS_ATTR_SIZE, +}; + struct gpiod_data { struct gpio_desc *desc; @@ -41,6 +50,15 @@ struct gpiod_data { unsigned char irq_flags; bool direction_can_change; + + struct device_attribute dir_attr; + struct device_attribute val_attr; + struct device_attribute edge_attr; + struct device_attribute active_low_attr; + + 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 gpiodev_data { @@ -79,7 +97,8 @@ static DEFINE_MUTEX(sysfs_lock); static ssize_t direction_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; int value; @@ -95,7 +114,8 @@ static ssize_t direction_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + dir_attr); struct gpio_desc *desc = data->desc; ssize_t status; @@ -112,12 +132,12 @@ static ssize_t direction_store(struct device *dev, return status ? : size; } -static DEVICE_ATTR_RW(direction); static ssize_t value_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; ssize_t status; @@ -133,7 +153,8 @@ static ssize_t value_show(struct device *dev, struct device_attribute *attr, static ssize_t value_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + val_attr); struct gpio_desc *desc = data->desc; ssize_t status; long value; @@ -150,7 +171,6 @@ static ssize_t value_store(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { @@ -247,7 +267,8 @@ static const char *const trigger_names[] = { static ssize_t edge_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); int flags; scoped_guard(mutex, &data->mutex) @@ -262,7 +283,8 @@ static ssize_t edge_show(struct device *dev, struct device_attribute *attr, static ssize_t edge_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + edge_attr); ssize_t status = size; int flags; @@ -289,7 +311,6 @@ static ssize_t edge_store(struct device *dev, struct device_attribute *attr, return size; } -static DEVICE_ATTR_RW(edge); /* Caller holds gpiod-data mutex. */ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) @@ -318,7 +339,8 @@ static int gpio_sysfs_set_active_low(struct gpiod_data *data, int value) static ssize_t active_low_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); struct gpio_desc *desc = data->desc; int value; @@ -332,7 +354,8 @@ static ssize_t active_low_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { - struct gpiod_data *data = dev_get_drvdata(dev); + struct gpiod_data *data = container_of(attr, struct gpiod_data, + active_low_attr); ssize_t status; long value; @@ -344,48 +367,34 @@ static ssize_t active_low_store(struct device *dev, return gpio_sysfs_set_active_low(data, value) ?: size; } -static DEVICE_ATTR_RW(active_low); static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = kobj_to_dev(kobj); - struct gpiod_data *data = dev_get_drvdata(dev); - struct gpio_desc *desc = data->desc; + struct device_attribute *dev_attr = container_of(attr, + struct device_attribute, attr); umode_t mode = attr->mode; - bool show_direction = data->direction_can_change; + struct gpiod_data *data; - if (attr == &dev_attr_direction.attr) { - if (!show_direction) + if (strcmp(attr->name, "direction") == 0) { + data = container_of(dev_attr, struct gpiod_data, dir_attr); + + if (!data->direction_can_change) mode = 0; - } else if (attr == &dev_attr_edge.attr) { - if (gpiod_to_irq(desc) < 0) + } else if (strcmp(attr->name, "edge") == 0) { + data = container_of(dev_attr, struct gpiod_data, edge_attr); + + if (gpiod_to_irq(data->desc) < 0) mode = 0; - if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags)) + + if (!data->direction_can_change && + test_bit(FLAG_IS_OUT, &data->desc->flags)) mode = 0; } return mode; } -static struct attribute *gpio_attrs[] = { - &dev_attr_direction.attr, - &dev_attr_edge.attr, - &dev_attr_value.attr, - &dev_attr_active_low.attr, - NULL, -}; - -static const struct attribute_group gpio_group = { - .attrs = gpio_attrs, - .is_visible = gpio_is_visible, -}; - -static const struct attribute_group *gpio_groups[] = { - &gpio_group, - NULL -}; - /* * /sys/class/gpio/gpiochipN/ * /base ... matching gpio_chip.base (N) @@ -645,6 +654,21 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock) return dev_get_drvdata(cdev); }; +static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf), + ssize_t (*store)(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count)) +{ + sysfs_attr_init(&dev_attr->attr); + dev_attr->attr.name = name; + dev_attr->attr.mode = 0644; + dev_attr->show = show; + dev_attr->store = store; +} + /** * gpiod_export - export a GPIO through sysfs * @desc: GPIO to make available, already requested @@ -665,6 +689,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) { struct gpiod_data *desc_data; struct gpio_device *gdev; + struct attribute **attrs; struct device *dev; int status; @@ -709,8 +734,26 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) else desc_data->direction_can_change = false; + gpiod_attr_init(&desc_data->dir_attr, "direction", + direction_show, direction_store); + gpiod_attr_init(&desc_data->val_attr, "value", value_show, value_store); + gpiod_attr_init(&desc_data->edge_attr, "edge", edge_show, edge_store); + gpiod_attr_init(&desc_data->active_low_attr, "active_low", + active_low_show, active_low_store); + + attrs = desc_data->class_attrs; + desc_data->class_attr_group.is_visible = gpio_is_visible; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_DIRECTION] = &desc_data->dir_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_VALUE] = &desc_data->val_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_EDGE] = &desc_data->edge_attr.attr; + attrs[GPIO_SYSFS_LINE_CLASS_ATTR_ACTIVE_LOW] = &desc_data->active_low_attr.attr; + + desc_data->class_attr_group.attrs = desc_data->class_attrs; + desc_data->class_attr_groups[0] = &desc_data->class_attr_group; + dev = device_create_with_groups(&gpio_class, &gdev->dev, - MKDEV(0, 0), desc_data, gpio_groups, + MKDEV(0, 0), desc_data, + desc_data->class_attr_groups, "gpio%u", desc_to_gpio(desc)); if (IS_ERR(dev)) { status = PTR_ERR(dev);