iio: core: create local __iio_chan_prefix_emit() for reuse

Move logic to create a channel prefix for naming attribute files into a
separate __iio_chan_prefix_emit() function for reuse. The dev pointer is
passed to __iio_device_attr_init() so that dev_err() replaces WARN().
The new helper is going to be used to expose channel relationships as
sysfs attributes.

Signed-off-by: Rodrigo Alencar <rodrigo.alencar@analog.com>
Signed-off-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
This commit is contained in:
Rodrigo Alencar 2026-07-29 09:25:33 +01:00 committed by Jonathan Cameron
parent 8267f6a0d6
commit 457dd51f3a

View File

@ -25,6 +25,7 @@
#include <linux/poll.h>
#include <linux/property.h>
#include <linux/sched.h>
#include <linux/seq_buf.h>
#include <linux/slab.h>
#include <linux/wait.h>
@ -200,6 +201,61 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_CONVDELAY] = "convdelay",
[IIO_CHAN_INFO_POWERFACTOR] = "powerfactor",
};
static ssize_t __iio_chan_prefix_emit(struct device *dev,
const struct iio_chan_spec *chan,
enum iio_shared_by shared_by,
char *buf, size_t len)
{
const char *type = iio_chan_type_name_spec[chan->type];
const char *dir = iio_direction[chan->output];
struct seq_buf s;
seq_buf_init(&s, buf, len);
switch (shared_by) {
case IIO_SHARED_BY_ALL:
break;
case IIO_SHARED_BY_DIR:
seq_buf_printf(&s, "%s", dir);
break;
case IIO_SHARED_BY_TYPE:
seq_buf_printf(&s, "%s_%s", dir, type);
if (chan->differential)
seq_buf_printf(&s, "-%s", type);
break;
case IIO_SEPARATE:
/* Validate differential channel settings */
if (chan->differential) {
if (!chan->indexed) {
dev_err(dev, "Differential channels must be indexed\n");
return -EINVAL;
}
if (chan->modified) {
dev_err(dev, "Differential channels can not have modifier\n");
return -EINVAL;
}
}
seq_buf_printf(&s, "%s_%s", dir, type);
if (chan->indexed)
seq_buf_printf(&s, "%d", chan->channel);
if (chan->differential)
seq_buf_printf(&s, "-%s%d", type, chan->channel2);
if (chan->modified)
seq_buf_printf(&s, "_%s", iio_modifier_names[chan->channel2]);
if (chan->extend_name)
seq_buf_printf(&s, "_%s", chan->extend_name);
break;
}
return seq_buf_has_overflowed(&s) ? -EOVERFLOW : s.len;
}
/**
* iio_device_id() - query the unique ID for the device
* @indio_dev: Device structure whose ID is being queried
@ -1092,7 +1148,8 @@ static ssize_t iio_write_channel_info(struct device *dev,
}
static
int __iio_device_attr_init(struct device_attribute *dev_attr,
int __iio_device_attr_init(struct device *dev,
struct device_attribute *dev_attr,
const char *postfix,
struct iio_chan_spec const *chan,
ssize_t (*readfunc)(struct device *dev,
@ -1104,106 +1161,22 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
size_t len),
enum iio_shared_by shared_by)
{
int ret = 0;
char *name = NULL;
char *full_postfix;
char prefix[NAME_MAX + 1];
ssize_t ret;
sysfs_attr_init(&dev_attr->attr);
/* Build up postfix of <extend_name>_<modifier>_postfix */
if (chan->modified && (shared_by == IIO_SEPARATE)) {
if (chan->extend_name)
full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_modifier_names[chan->channel2],
chan->extend_name,
postfix);
else
full_postfix = kasprintf(GFP_KERNEL, "%s_%s",
iio_modifier_names[chan->channel2],
postfix);
} else {
if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)
full_postfix = kstrdup(postfix, GFP_KERNEL);
else
full_postfix = kasprintf(GFP_KERNEL,
"%s_%s",
chan->extend_name,
postfix);
}
if (full_postfix == NULL)
ret = __iio_chan_prefix_emit(dev, chan, shared_by, prefix, sizeof(prefix));
if (ret < 0)
return ret;
if (ret)
dev_attr->attr.name = kasprintf(GFP_KERNEL, "%s_%s", prefix, postfix);
else
dev_attr->attr.name = kstrdup(postfix, GFP_KERNEL);
if (!dev_attr->attr.name)
return -ENOMEM;
if (chan->differential) { /* Differential can not have modifier */
switch (shared_by) {
case IIO_SHARED_BY_ALL:
name = kasprintf(GFP_KERNEL, "%s", full_postfix);
break;
case IIO_SHARED_BY_DIR:
name = kasprintf(GFP_KERNEL, "%s_%s",
iio_direction[chan->output],
full_postfix);
break;
case IIO_SHARED_BY_TYPE:
name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
iio_chan_type_name_spec[chan->type],
full_postfix);
break;
case IIO_SEPARATE:
if (!chan->indexed) {
WARN(1, "Differential channels must be indexed\n");
ret = -EINVAL;
goto error_free_full_postfix;
}
name = kasprintf(GFP_KERNEL,
"%s_%s%d-%s%d_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
chan->channel,
iio_chan_type_name_spec[chan->type],
chan->channel2,
full_postfix);
break;
}
} else { /* Single ended */
switch (shared_by) {
case IIO_SHARED_BY_ALL:
name = kasprintf(GFP_KERNEL, "%s", full_postfix);
break;
case IIO_SHARED_BY_DIR:
name = kasprintf(GFP_KERNEL, "%s_%s",
iio_direction[chan->output],
full_postfix);
break;
case IIO_SHARED_BY_TYPE:
name = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
full_postfix);
break;
case IIO_SEPARATE:
if (chan->indexed)
name = kasprintf(GFP_KERNEL, "%s_%s%d_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
chan->channel,
full_postfix);
else
name = kasprintf(GFP_KERNEL, "%s_%s_%s",
iio_direction[chan->output],
iio_chan_type_name_spec[chan->type],
full_postfix);
break;
}
}
if (name == NULL) {
ret = -ENOMEM;
goto error_free_full_postfix;
}
dev_attr->attr.name = name;
if (readfunc) {
dev_attr->attr.mode |= 0444;
dev_attr->show = readfunc;
@ -1214,10 +1187,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
dev_attr->store = writefunc;
}
error_free_full_postfix:
kfree(full_postfix);
return ret;
return 0;
}
static void __iio_device_attr_deinit(struct device_attribute *dev_attr)
@ -1246,7 +1216,7 @@ int __iio_add_chan_devattr(const char *postfix,
iio_attr = kzalloc_obj(*iio_attr);
if (iio_attr == NULL)
return -ENOMEM;
ret = __iio_device_attr_init(&iio_attr->dev_attr,
ret = __iio_device_attr_init(dev, &iio_attr->dev_attr,
postfix, chan,
readfunc, writefunc, shared_by);
if (ret)