@@ -32,6 +32,15 @@ struct kernfs_node;
#define GPIO_IRQF_TRIGGER_BOTH (GPIO_IRQF_TRIGGER_FALLING | \
GPIO_IRQF_TRIGGER_RISING)
+enum {
+ GPIO_SYSFS_LINE_ATTR_DIRECTION = 0,
+ GPIO_SYSFS_LINE_ATTR_VALUE,
+ GPIO_SYSFS_LINE_ATTR_EDGE,
+ GPIO_SYSFS_LINE_ATTR_ACTIVE_LOW,
+ GPIO_SYSFS_LINE_ATTR_SENTINEL,
+ GPIO_SYSFS_LINE_ATTR_SIZE,
+};
+
struct gpiod_data {
struct gpio_desc *desc;
@@ -41,6 +50,14 @@ 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 *attrs[GPIO_SYSFS_LINE_ATTR_SIZE];
+ struct attribute_group attr_group;
+ const struct attribute_group *attr_groups[2];
};
struct gpiodev_data {
@@ -79,7 +96,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 +113,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 +131,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 +152,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 +170,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 +266,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 +282,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 +310,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 +338,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 +353,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 +366,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 +653,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
@@ -664,6 +687,7 @@ gdev_get_data(struct gpio_device *gdev) __must_hold(&sysfs_lock)
int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
struct gpio_device *gdev;
+ struct attribute **attrs;
struct gpiod_data *data;
struct device *dev;
int status;
@@ -709,8 +733,26 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
else
data->direction_can_change = false;
+ gpiod_attr_init(&data->dir_attr, "direction",
+ direction_show, direction_store);
+ gpiod_attr_init(&data->val_attr, "value", value_show, value_store);
+ gpiod_attr_init(&data->edge_attr, "edge", edge_show, edge_store);
+ gpiod_attr_init(&data->active_low_attr, "active_low",
+ active_low_show, active_low_store);
+
+ attrs = data->attrs;
+ data->attr_group.is_visible = gpio_is_visible;
+ attrs[GPIO_SYSFS_LINE_ATTR_DIRECTION] = &data->dir_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_VALUE] = &data->val_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_EDGE] = &data->edge_attr.attr;
+ attrs[GPIO_SYSFS_LINE_ATTR_ACTIVE_LOW] =
+ &data->active_low_attr.attr;
+
+ data->attr_group.attrs = data->attrs;
+ data->attr_groups[0] = &data->attr_group;
+
dev = device_create_with_groups(&gpio_class, &gdev->dev,
- MKDEV(0, 0), data, gpio_groups,
+ MKDEV(0, 0), data, data->attr_groups,
"gpio%u", desc_to_gpio(desc));
if (IS_ERR(dev)) {
status = PTR_ERR(dev);